From f00b77c468e3e692b6ebf5f2048469c8bc6c8e20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 01:39:33 +0000 Subject: [PATCH 1/4] Bump mocha from 10.8.2 to 11.0.1 Bumps [mocha](https://github.com/mochajs/mocha) from 10.8.2 to 11.0.1. - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v10.8.2...v11.0.1) --- updated-dependencies: - dependency-name: mocha dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 107 +++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index e8d463bb1..3467c83f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,7 +101,7 @@ "eslint-config-oclif-typescript": "^1.0.3", "eslint-plugin-unicorn": "^56.0.0", "marked": "^15.0.0", - "mocha": "^10", + "mocha": "^11", "mock-fs": "^5.2.0", "oclif": "^4.8.8", "tmp": "^0.2.1", @@ -12670,14 +12670,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/js-beautify/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", @@ -13418,6 +13410,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mixme": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/mixme/-/mixme-0.5.10.tgz", @@ -13427,9 +13427,9 @@ } }, "node_modules/mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.0.1.tgz", + "integrity": "sha512-+3GkODfsDG71KSCQhc4IekSW+ItCK/kiez1Z28ksWvYhKXV/syxMlerR/sC7whDp7IyreZ4YxceMLdTs5hQE8A==", "dev": true, "dependencies": { "ansi-colors": "^4.1.3", @@ -13439,7 +13439,7 @@ "diff": "^5.2.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", - "glob": "^8.1.0", + "glob": "^10.4.5", "he": "^1.2.0", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", @@ -13458,7 +13458,7 @@ "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/mocha/node_modules/chalk": { @@ -13510,24 +13510,55 @@ } }, "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=12" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mocha/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/mocha/node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -16581,6 +16612,12 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -16707,15 +16744,15 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -16729,14 +16766,6 @@ "node": "14 || >=16.14" } }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", @@ -18356,14 +18385,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/sucrase/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", diff --git a/package.json b/package.json index 977f72031..88bab7eb1 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "eslint-config-oclif-typescript": "^1.0.3", "eslint-plugin-unicorn": "^56.0.0", "marked": "^15.0.0", - "mocha": "^10", + "mocha": "^11", "mock-fs": "^5.2.0", "oclif": "^4.8.8", "tmp": "^0.2.1", From 76e29a07682dbe483f1adfa4f6642e393599ae65 Mon Sep 17 00:00:00 2001 From: George M Dias Date: Wed, 12 Mar 2025 19:34:35 -0500 Subject: [PATCH 2/4] mocha version 10 to 11 Signed-off-by: George M Dias --- VERSION | 2 +- package-lock.json | 9971 +++-- package.json | 37 +- release-prep.ps1 | 7 +- release-prep.sh | 4 +- src/commands/emasser/configure.ts | 31 +- src/commands/emasser/delete/artifacts.ts | 21 +- .../emasser/delete/cloud_resources.ts | 53 + .../emasser/delete/container_scans.ts | 53 + .../emasser/delete/hardware_baseline.ts | 51 + src/commands/emasser/delete/milestones.ts | 22 +- src/commands/emasser/delete/poams.ts | 26 +- .../emasser/delete/software_baseline.ts | 51 + src/commands/emasser/get/artifacts.ts | 40 +- src/commands/emasser/get/cac.ts | 19 +- src/commands/emasser/get/cmmc.ts | 19 +- src/commands/emasser/get/controls.ts | 19 +- src/commands/emasser/get/dashboards.ts | 491 +- src/commands/emasser/get/hardware.ts | 64 + src/commands/emasser/get/milestones.ts | 14 +- src/commands/emasser/get/pac.ts | 19 +- src/commands/emasser/get/poams.ts | 14 +- src/commands/emasser/get/roles.ts | 16 +- src/commands/emasser/get/software.ts | 64 + src/commands/emasser/get/system.ts | 19 +- src/commands/emasser/get/systems.ts | 27 +- src/commands/emasser/get/test_connection.ts | 19 +- src/commands/emasser/get/test_results.ts | 19 +- .../emasser/get/workflow_definitions.ts | 10 +- .../emasser/get/workflow_instances.ts | 14 +- src/commands/emasser/hello.ts | 7 +- src/commands/emasser/post/artifacts.ts | 84 +- src/commands/emasser/post/cac.ts | 21 +- src/commands/emasser/post/cloud_resources.ts | 135 +- src/commands/emasser/post/container_scans.ts | 205 +- src/commands/emasser/post/device_scans.ts | 96 + .../emasser/post/hardware_baseline.ts | 311 + src/commands/emasser/post/milestones.ts | 23 +- src/commands/emasser/post/pac.ts | 21 +- src/commands/emasser/post/poams.ts | 390 +- src/commands/emasser/post/register.ts | 19 +- .../emasser/post/software_baseline.ts | 413 + .../emasser/post/static_code_scans.ts | 136 +- src/commands/emasser/post/test_results.ts | 26 +- src/commands/emasser/put/artifacts.ts | 164 +- src/commands/emasser/put/controls.ts | 126 +- src/commands/emasser/put/hardware_baseline.ts | 313 + src/commands/emasser/put/milestones.ts | 22 +- src/commands/emasser/put/poams.ts | 454 +- src/commands/emasser/put/software_baseline.ts | 430 + src/commands/generate/delta.ts | 8 +- src/commands/generate/inspec_profile.ts | 28 +- .../generate/update_controls4delta.ts | 2 +- src/commands/validate/threshold.ts | 125 +- src/utils/emasser/apiConfig.ts | 84 +- src/utils/emasser/apiConnection.ts | 16 +- src/utils/emasser/generateConfig.ts | 291 +- src/utils/emasser/initConnection.ts | 49 +- src/utils/emasser/outputError.ts | 22 +- src/utils/emasser/outputFormatter.ts | 36 +- src/utils/emasser/utilities.ts | 1035 +- src/utils/oclif/hooks/command_not_found.ts | 28 + src/utils/threshold.ts | 13 + test/commands/attest/apply.test.ts | 122 +- test/commands/convert/ckl2hdf.test.ts | 4 +- test/commands/convert/trufflehog2hdf.test.ts | 10 + test/commands/emasser/delete.test.ts | 98 +- test/commands/emasser/get.test.ts | 467 +- test/commands/emasser/post.test.ts | 134 +- test/commands/emasser/put.test.ts | 69 +- test/commands/generate/delta.test.ts | 36 +- test/commands/generate/inspec_profile.test.ts | 13 +- .../input/triple_overlay_profile_sample.json | 1247 + .../attestations/triple_overlay_attested.json | 1275 + .../triple_overlay_example-attestations.json | 19 + .../triple_overlay_example-attestations.yml | 12 + .../sample_input_report/trufflehog_dup.ndjson | 6 + .../trufflehog/trufflehog-ndjson-dup-hdf.json | 94 + ..._AlmaLinux_OS_9_Benchmark_v2.0.0-xccdf.xml | 30340 ++++++++++++++++ ..._Amazon_Linux_2_Benchmark_v3.0.0-xccdf.xml | 26339 ++++++++++++++ ...pache_Tomcat_10_Benchmark_v1.1.0-xccdf.xml | 3931 ++ ...icrosoft_IIS_10_Benchmark_v1.2.1-xccdf.xml | 4405 +++ 82 files changed, 77793 insertions(+), 7177 deletions(-) create mode 100644 src/commands/emasser/delete/cloud_resources.ts create mode 100644 src/commands/emasser/delete/container_scans.ts create mode 100644 src/commands/emasser/delete/hardware_baseline.ts create mode 100644 src/commands/emasser/delete/software_baseline.ts create mode 100644 src/commands/emasser/get/hardware.ts create mode 100644 src/commands/emasser/get/software.ts create mode 100644 src/commands/emasser/post/device_scans.ts create mode 100644 src/commands/emasser/post/hardware_baseline.ts create mode 100644 src/commands/emasser/post/software_baseline.ts create mode 100644 src/commands/emasser/put/hardware_baseline.ts create mode 100644 src/commands/emasser/put/software_baseline.ts create mode 100644 src/utils/oclif/hooks/command_not_found.ts create mode 100644 test/sample_data/HDF/input/triple_overlay_profile_sample.json create mode 100644 test/sample_data/attestations/triple_overlay_attested.json create mode 100644 test/sample_data/attestations/triple_overlay_example-attestations.json create mode 100644 test/sample_data/attestations/triple_overlay_example-attestations.yml create mode 100644 test/sample_data/trufflehog/sample_input_report/trufflehog_dup.ndjson create mode 100644 test/sample_data/trufflehog/trufflehog-ndjson-dup-hdf.json create mode 100644 test/sample_data/xccdf/cis/CIS_AlmaLinux_OS_9_Benchmark_v2.0.0-xccdf.xml create mode 100644 test/sample_data/xccdf/cis/CIS_Amazon_Linux_2_Benchmark_v3.0.0-xccdf.xml create mode 100644 test/sample_data/xccdf/cis/CIS_Apache_Tomcat_10_Benchmark_v1.1.0-xccdf.xml create mode 100644 test/sample_data/xccdf/cis/CIS_Microsoft_IIS_10_Benchmark_v1.2.1-xccdf.xml diff --git a/VERSION b/VERSION index 04e0d3f14..8cc8fe024 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.17 +1.4.21 diff --git a/package-lock.json b/package-lock.json index 62e7a557e..426f9f7eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mitre/saf", - "version": "1.4.17", + "version": "1.4.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@mitre/saf", - "version": "1.4.17", + "version": "1.4.23", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-config-service": "^3.398.0", @@ -14,17 +14,17 @@ "@azure/identity": "^4.3.0", "@microsoft/microsoft-graph-client": "^3.0.7", "@microsoft/microsoft-graph-types": "^2.40.0", - "@mitre/emass_client": "3.10.0", - "@mitre/hdf-converters": "2.11.1", - "@mitre/heimdall-lite": "2.11.1", - "@mitre/inspec-objects": "1.0.1", + "@mitre/emass_client": "3.22.0", + "@mitre/hdf-converters": "2.11.5", + "@mitre/heimdall-lite": "2.11.5", + "@mitre/inspec-objects": "2.0.1", "@oclif/core": "^4.0.15", "@oclif/plugin-help": "^6.0.9", "@oclif/plugin-plugins": "^5.0.14", "@oclif/plugin-version": "^2.0.11", "@oclif/plugin-warn-if-update-available": "^3.0.15", - "@smithy/node-http-handler": "^3.0.0", - "@types/chai": "^4", + "@smithy/node-http-handler": "^4.0.0", + "@types/chai": "4.3.3", "@types/express": "^5.0.0", "@types/fs-extra": "^11.0.1", "@types/get-installed-path": "^4.0.1", @@ -42,13 +42,13 @@ "ajv": "^8.12.0", "app-root-path": "^3.1.0", "axios": "^1.5.0", - "chai": "^4", + "chai": "4.5.0", "chalk": "^5.4.1", "colors": "^1.4.0", "csv-parse": "^4.16.0", "dotenv": "^16.3.1", "express": "^4.17.3", - "fast-xml-parser": "^4.2.7", + "fast-xml-parser": "^5.0.7", "flat": "^6.0.1", "form-data": "^4.0.0", "fs-extra": "^11.1.1", @@ -56,13 +56,12 @@ "get-installed-path": "^4.0.8", "htmlparser2": "^10.0.0", "https": "^1.0.0", - "inquirer": "12.3.0", + "inquirer": "12.4.3", "inquirer-file-selector": "^0.6.1", - "inspecjs": "^2.10.16", + "inspecjs": "2.11.0", "jest": "^29.7.0", "jest-mock": "^29.7.0", "js-yaml": "^4.1.0", - "jsdom": "^25.0.1", "json-colorizer": "^3.0.1", "lodash": "^4.17.21", "markdown-diff": "^2.0.0", @@ -78,7 +77,7 @@ "ts-node": "^10", "tsimportlib": "^0.0.5", "tslib": "^2", - "typescript": "~5.7", + "typescript": "~5.8", "uuid": "^11.0.2", "winston": "^3.10.0", "xlsx-populate": "^1.21.0", @@ -100,37 +99,18 @@ "eslint-config-oclif": "^4.0", "eslint-config-oclif-typescript": "^1.0.3", "eslint-plugin-unicorn": "^56.0.0", + "jsdom": "^26.0.0", "marked": "^15.0.0", "mocha": "^11", "mock-fs": "^5.2.0", "oclif": "^4.8.8", "tmp": "^0.2.1", - "ts-mocha": "^10.0.0" + "ts-mocha": "^11.1.0" }, "engines": { "node": "^22.0.0" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -143,6 +123,25 @@ "node": ">=6.0.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.1.tgz", + "integrity": "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA==", + "dev": true, + "dependencies": { + "@csstools/css-calc": "^2.1.2", + "@csstools/css-color-parser": "^3.0.8", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", @@ -336,946 +335,290 @@ } }, "node_modules/@aws-sdk/client-cloudfront": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudfront/-/client-cloudfront-3.716.0.tgz", - "integrity": "sha512-r9m+gxgjh/Bt+W3z+F8iR9W0TwBCJQIHt9gjyfIzWa2tLFGzrAajFfs5RUrWpFyl+e8Q6wukAXXm9QhQDwTOsQ==", + "version": "3.764.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudfront/-/client-cloudfront-3.764.0.tgz", + "integrity": "sha512-nXKaM9/T9viu5IXcPueTjf10VHOMX4J1FHWITDdk0s/vY2YZidGAZmeHLA0QXM0SxOQw/xga4d4k5HKdup2DSw==", "dev": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.716.0", - "@aws-sdk/client-sts": "3.716.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-node": "3.716.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.716.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.716.0", - "@aws-sdk/xml-builder": "3.709.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-stream": "^3.3.2", - "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/credential-provider-node": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@aws-sdk/xml-builder": "3.734.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/client-config-service": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-config-service/-/client-config-service-3.721.0.tgz", - "integrity": "sha512-aK+QP2upi9JI5ieCq09A3Ae3se4SjdBHiRXHFyhYudRdSHUdC8KhMiBGjXgHRtpY6NxiT1x3M1Bxd5fAl2bwsA==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.721.0", - "@aws-sdk/client-sts": "3.721.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-node": "3.721.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.721.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.721.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-config-service/node_modules/@aws-sdk/client-sso": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.721.0.tgz", - "integrity": "sha512-UrYAF4ilpO2cZBFddQmbETfo0xKP3CEcantcMQTc0xPY3quHLZhYuBiRae+McWi6yZpH4ErnFZIWeKSJ2OQgqQ==", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-config-service/-/client-config-service-3.758.0.tgz", + "integrity": "sha512-Pf+AK3U6fr3uXzm4Hi7xz1nLDV1Sb5dLTspobpCG5L2wEGXnIpioE2rXlubzs0drfH6mJ1huVF4ZseeYFmeReA==", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.721.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.721.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-config-service/node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.721.0.tgz", - "integrity": "sha512-jwsgdUEbNJqs1O0AQtf9M6SI7hFIjxH+IKeKCMca0xVt+Tr1UqLr/qMK/6W8LoMtRFnE0lpBSHW6hvmLp2OCoQ==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-node": "3.721.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.721.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.721.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.721.0" - } - }, - "node_modules/@aws-sdk/client-config-service/node_modules/@aws-sdk/client-sts": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.721.0.tgz", - "integrity": "sha512-1Pv8F02hQFmPZs7WtGfQNlnInbG1lLzyngJc/MlZ3Ld2fIoWjaWp7bJWgYAjnzHNEuDtCabWJvIfePdRqsbYoA==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.721.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-node": "3.721.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.721.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.721.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-config-service/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.721.0.tgz", - "integrity": "sha512-8J/c2rI+4ZoduBCnPurfdblqs2DyRvL9ztqzzOWWEhLccoYZzYeAMwBapEAsiVsD1iNrIGY7LRDC4TsVmJBf6Q==", - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-env": "3.716.0", - "@aws-sdk/credential-provider-http": "3.716.0", - "@aws-sdk/credential-provider-process": "3.716.0", - "@aws-sdk/credential-provider-sso": "3.721.0", - "@aws-sdk/credential-provider-web-identity": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.721.0" - } - }, - "node_modules/@aws-sdk/client-config-service/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.721.0.tgz", - "integrity": "sha512-D6xodzdMjVhF9xRhy9gNf0gqP0Dek9fQ6BDZzqO/i54d7CjWHVZTADcVcxjLQq6nyUNf0QPf8UXLaqi+w25GGQ==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.716.0", - "@aws-sdk/credential-provider-http": "3.716.0", - "@aws-sdk/credential-provider-ini": "3.721.0", - "@aws-sdk/credential-provider-process": "3.716.0", - "@aws-sdk/credential-provider-sso": "3.721.0", - "@aws-sdk/credential-provider-web-identity": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/credential-provider-node": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-config-service/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.721.0.tgz", - "integrity": "sha512-v7npnYqfuY1vdcb0/F4Mcz+mcFyZaYry9qXhSRCPIbLPe2PRV4E4HXIaPKmir8PhuRLEGs0QJWhvIWr7u6holQ==", - "dependencies": { - "@aws-sdk/client-sso": "3.721.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/token-providers": "3.721.0", - "@aws-sdk/types": "3.714.0", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-config-service/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.721.0.tgz", - "integrity": "sha512-Z3Vksb970ArsfLlARW4KVpqO+pQ1cvvGTrTQPxWDsmOzg1kU92t9oWXGW+1M/x6bHbMQlI/EulQ/D8ZE/Pu46Q==", - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@smithy/core": "^2.5.5", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-config-service/node_modules/@aws-sdk/token-providers": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.721.0.tgz", - "integrity": "sha512-cIZmKdLeEWUzPR+2lA+JcZHPvaFf/Ih+s3LXBa/uQwRFdK+o7WfGRf7Oqe6yLRekO2jJJl4LBJXxDOH++M9+ag==", - "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.721.0" - } - }, - "node_modules/@aws-sdk/client-config-service/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.721.0.tgz", - "integrity": "sha512-5VsNdC3zQnjrt7KNEeFHWJl3FIamgIS0puG18BMvPsdzcKWEbWDih+yd1kMWrcpAu1Riez9co/gB9y99pBghDA==", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.721.0", - "@aws-sdk/types": "3.714.0", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.717.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.717.0.tgz", - "integrity": "sha512-jzaH8IskAXVnqlZ3/H/ROwrB2HCnq/atlN7Hi7FIfjWvMPf5nfcJKfzJ1MXFX0EQR5qO6X4TbK7rgi7Bjw9NjQ==", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.758.0.tgz", + "integrity": "sha512-f8SlhU9/93OC/WEI6xVJf/x/GoQFj9a/xXK6QCtr5fvCjfSLgMVFmKTiIl/tgtDRzxUDc8YS6EGtbHjJ3Y/atg==", "dev": true, "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.716.0", - "@aws-sdk/client-sts": "3.716.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-node": "3.716.0", - "@aws-sdk/middleware-bucket-endpoint": "3.714.0", - "@aws-sdk/middleware-expect-continue": "3.714.0", - "@aws-sdk/middleware-flexible-checksums": "3.717.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-location-constraint": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-sdk-s3": "3.716.0", - "@aws-sdk/middleware-ssec": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.716.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/signature-v4-multi-region": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.716.0", - "@aws-sdk/xml-builder": "3.709.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/eventstream-serde-browser": "^3.0.14", - "@smithy/eventstream-serde-config-resolver": "^3.0.11", - "@smithy/eventstream-serde-node": "^3.0.13", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-blob-browser": "^3.1.10", - "@smithy/hash-node": "^3.0.11", - "@smithy/hash-stream-node": "^3.1.10", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/md5-js": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-stream": "^3.3.2", - "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/credential-provider-node": "3.758.0", + "@aws-sdk/middleware-bucket-endpoint": "3.734.0", + "@aws-sdk/middleware-expect-continue": "3.734.0", + "@aws-sdk/middleware-flexible-checksums": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-location-constraint": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-sdk-s3": "3.758.0", + "@aws-sdk/middleware-ssec": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/signature-v4-multi-region": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@aws-sdk/xml-builder": "3.734.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/eventstream-serde-browser": "^4.0.1", + "@smithy/eventstream-serde-config-resolver": "^4.0.1", + "@smithy/eventstream-serde-node": "^4.0.1", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-blob-browser": "^4.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/hash-stream-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/md5-js": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/client-securityhub": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-securityhub/-/client-securityhub-3.721.0.tgz", - "integrity": "sha512-SSV04Uzn2GzZXNiEzl/zzxlA4esVU1QwAdtLII08/17s3A1AVD930AWpMVj6h7+PjiTd53qpf0/O+Pr/DpHi5g==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.721.0", - "@aws-sdk/client-sts": "3.721.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-node": "3.721.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.721.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.721.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-securityhub/node_modules/@aws-sdk/client-sso": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.721.0.tgz", - "integrity": "sha512-UrYAF4ilpO2cZBFddQmbETfo0xKP3CEcantcMQTc0xPY3quHLZhYuBiRae+McWi6yZpH4ErnFZIWeKSJ2OQgqQ==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.721.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.721.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-securityhub/node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.721.0.tgz", - "integrity": "sha512-jwsgdUEbNJqs1O0AQtf9M6SI7hFIjxH+IKeKCMca0xVt+Tr1UqLr/qMK/6W8LoMtRFnE0lpBSHW6hvmLp2OCoQ==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-node": "3.721.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.721.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.721.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.721.0" - } - }, - "node_modules/@aws-sdk/client-securityhub/node_modules/@aws-sdk/client-sts": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.721.0.tgz", - "integrity": "sha512-1Pv8F02hQFmPZs7WtGfQNlnInbG1lLzyngJc/MlZ3Ld2fIoWjaWp7bJWgYAjnzHNEuDtCabWJvIfePdRqsbYoA==", + "version": "3.765.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-securityhub/-/client-securityhub-3.765.0.tgz", + "integrity": "sha512-H/8tftWfmnZGvVDMOnAn+PxfF/OdE9aqlvi1jfcBHhqu1FgZu5vF3Gzg4uTVOIqM3tvMd7jEE2CswpFINNwCpg==", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.721.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-node": "3.721.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.721.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.721.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-securityhub/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.721.0.tgz", - "integrity": "sha512-8J/c2rI+4ZoduBCnPurfdblqs2DyRvL9ztqzzOWWEhLccoYZzYeAMwBapEAsiVsD1iNrIGY7LRDC4TsVmJBf6Q==", - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-env": "3.716.0", - "@aws-sdk/credential-provider-http": "3.716.0", - "@aws-sdk/credential-provider-process": "3.716.0", - "@aws-sdk/credential-provider-sso": "3.721.0", - "@aws-sdk/credential-provider-web-identity": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.721.0" - } - }, - "node_modules/@aws-sdk/client-securityhub/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.721.0.tgz", - "integrity": "sha512-D6xodzdMjVhF9xRhy9gNf0gqP0Dek9fQ6BDZzqO/i54d7CjWHVZTADcVcxjLQq6nyUNf0QPf8UXLaqi+w25GGQ==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.716.0", - "@aws-sdk/credential-provider-http": "3.716.0", - "@aws-sdk/credential-provider-ini": "3.721.0", - "@aws-sdk/credential-provider-process": "3.716.0", - "@aws-sdk/credential-provider-sso": "3.721.0", - "@aws-sdk/credential-provider-web-identity": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-securityhub/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.721.0.tgz", - "integrity": "sha512-v7npnYqfuY1vdcb0/F4Mcz+mcFyZaYry9qXhSRCPIbLPe2PRV4E4HXIaPKmir8PhuRLEGs0QJWhvIWr7u6holQ==", - "dependencies": { - "@aws-sdk/client-sso": "3.721.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/token-providers": "3.721.0", - "@aws-sdk/types": "3.714.0", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-securityhub/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.721.0.tgz", - "integrity": "sha512-Z3Vksb970ArsfLlARW4KVpqO+pQ1cvvGTrTQPxWDsmOzg1kU92t9oWXGW+1M/x6bHbMQlI/EulQ/D8ZE/Pu46Q==", - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@smithy/core": "^2.5.5", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-securityhub/node_modules/@aws-sdk/token-providers": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.721.0.tgz", - "integrity": "sha512-cIZmKdLeEWUzPR+2lA+JcZHPvaFf/Ih+s3LXBa/uQwRFdK+o7WfGRf7Oqe6yLRekO2jJJl4LBJXxDOH++M9+ag==", - "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.721.0" - } - }, - "node_modules/@aws-sdk/client-securityhub/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.721.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.721.0.tgz", - "integrity": "sha512-5VsNdC3zQnjrt7KNEeFHWJl3FIamgIS0puG18BMvPsdzcKWEbWDih+yd1kMWrcpAu1Riez9co/gB9y99pBghDA==", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.721.0", - "@aws-sdk/types": "3.714.0", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/credential-provider-node": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.716.0.tgz", - "integrity": "sha512-5Nb0jJXce2TclbjG7WVPufwhgV1TRydz1QnsuBtKU0AdViEpr787YrZhPpGnNIM1Dx+R1H/tmAHZnOoohS6D8g==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.716.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.716.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-sdk/client-sso-oidc": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.716.0.tgz", - "integrity": "sha512-lA4IB9FzR2KjH7EVCo+mHGFKqdViVyeBQEIX9oVratL/l7P0bMS1fMwgfHOc3ACazqNxBxDES7x08ZCp32y6Lw==", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-node": "3.716.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.716.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.716.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.716.0" - } - }, - "node_modules/@aws-sdk/client-sts": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.716.0.tgz", - "integrity": "sha512-i4SVNsrdXudp8T4bkm7Fi3YWlRnvXCSwvNDqf6nLqSJxqr4CN3VlBELueDyjBK7TAt453/qSif+eNx+bHmwo4Q==", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.758.0.tgz", + "integrity": "sha512-BoGO6IIWrLyLxQG6txJw6RT2urmbtlwfggapNCrNPyYjlXpzTSJhBYjndg7TpDATFd0SXL0zm8y/tXsUXNkdYQ==", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/client-sso-oidc": "3.716.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-node": "3.716.0", - "@aws-sdk/middleware-host-header": "3.714.0", - "@aws-sdk/middleware-logger": "3.714.0", - "@aws-sdk/middleware-recursion-detection": "3.714.0", - "@aws-sdk/middleware-user-agent": "3.716.0", - "@aws-sdk/region-config-resolver": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@aws-sdk/util-user-agent-browser": "3.714.0", - "@aws-sdk/util-user-agent-node": "3.716.0", - "@smithy/config-resolver": "^3.0.13", - "@smithy/core": "^2.5.5", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/hash-node": "^3.0.11", - "@smithy/invalid-dependency": "^3.0.11", - "@smithy/middleware-content-length": "^3.0.13", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-retry": "^3.0.31", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-body-length-node": "^3.0.0", - "@smithy/util-defaults-mode-browser": "^3.0.31", - "@smithy/util-defaults-mode-node": "^3.0.31", - "@smithy/util-endpoints": "^2.1.7", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", - "@smithy/util-utf8": "^3.0.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/core": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.716.0.tgz", - "integrity": "sha512-5DkUiTrbyzO8/W4g7UFEqRFpuhgizayHI/Zbh0wtFMcot8801nJV+MP/YMhdjimlvAr/OqYB08FbGsPyWppMTw==", - "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/core": "^2.5.5", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/signature-v4": "^4.2.4", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/util-middleware": "^3.0.11", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.758.0.tgz", + "integrity": "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/core/node_modules/fast-xml-parser": { @@ -1299,432 +642,485 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/@aws-sdk/core/node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] + }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.716.0.tgz", - "integrity": "sha512-JI2KQUnn2arICwP9F3CnqP1W3nAbm4+meQg/yOhp9X0DMzQiHrHRd4HIrK2vyVgi2/6hGhONY5uLF26yRTA7nQ==", - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/property-provider": "^3.1.11", - "@smithy/types": "^3.7.2", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.758.0.tgz", + "integrity": "sha512-N27eFoRrO6MeUNumtNHDW9WOiwfd59LPXPqDrIa3kWL/s+fOKFHb9xIcF++bAwtcZnAxKkgpDCUP+INNZskE+w==", + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.716.0.tgz", - "integrity": "sha512-CZ04pl2z7igQPysQyH2xKZHM3fLwkemxQbKOlje3TmiS1NwXvcKvERhp9PE/H23kOL7beTM19NMRog/Fka/rlw==", - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/property-provider": "^3.1.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/util-stream": "^3.3.2", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.758.0.tgz", + "integrity": "sha512-Xt9/U8qUCiw1hihztWkNeIR+arg6P+yda10OuCHX6kFVx3auTlU7+hCqs3UxqniGU4dguHuftf3mRpi5/GJ33Q==", + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.716.0.tgz", - "integrity": "sha512-P37We2GtZvdROxiwP0zrpEL81/HuYK1qlYxp5VCj3uV+G4mG8UQN2gMIU/baYrpOQqa0h81RfyQGRFUjVaDVqw==", - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/credential-provider-env": "3.716.0", - "@aws-sdk/credential-provider-http": "3.716.0", - "@aws-sdk/credential-provider-process": "3.716.0", - "@aws-sdk/credential-provider-sso": "3.716.0", - "@aws-sdk/credential-provider-web-identity": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.758.0.tgz", + "integrity": "sha512-cymSKMcP5d+OsgetoIZ5QCe1wnp2Q/tq+uIxVdh9MbfdBBEnl9Ecq6dH6VlYS89sp4QKuxHxkWXVnbXU3Q19Aw==", + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/credential-provider-env": "3.758.0", + "@aws-sdk/credential-provider-http": "3.758.0", + "@aws-sdk/credential-provider-process": "3.758.0", + "@aws-sdk/credential-provider-sso": "3.758.0", + "@aws-sdk/credential-provider-web-identity": "3.758.0", + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.716.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.716.0.tgz", - "integrity": "sha512-FGQPK2uKfS53dVvoskN/s/t6m0Po24BGd1PzJdzHBFCOjxbZLM6+8mDMXeyi2hCLVVQOUcuW41kOgmJ0+zMbww==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.716.0", - "@aws-sdk/credential-provider-http": "3.716.0", - "@aws-sdk/credential-provider-ini": "3.716.0", - "@aws-sdk/credential-provider-process": "3.716.0", - "@aws-sdk/credential-provider-sso": "3.716.0", - "@aws-sdk/credential-provider-web-identity": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.758.0.tgz", + "integrity": "sha512-+DaMv63wiq7pJrhIQzZYMn4hSarKiizDoJRvyR7WGhnn0oQ/getX9Z0VNCV3i7lIFoLNTb7WMmQ9k7+z/uD5EQ==", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.758.0", + "@aws-sdk/credential-provider-http": "3.758.0", + "@aws-sdk/credential-provider-ini": "3.758.0", + "@aws-sdk/credential-provider-process": "3.758.0", + "@aws-sdk/credential-provider-sso": "3.758.0", + "@aws-sdk/credential-provider-web-identity": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.716.0.tgz", - "integrity": "sha512-0spcu2MWVVHSTHH3WE2E//ttUJPwXRM3BCp+WyI41xLzpNu1Fd8zjOrDpEo0SnGUzsSiRTIJWgkuu/tqv9NJ2A==", - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.758.0.tgz", + "integrity": "sha512-AzcY74QTPqcbXWVgjpPZ3HOmxQZYPROIBz2YINF0OQk0MhezDWV/O7Xec+K1+MPGQO3qS6EDrUUlnPLjsqieHA==", + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.716.0.tgz", - "integrity": "sha512-J2IA3WuCpRGGoZm6VHZVFCnrxXP+41iUWb9Ct/1spljegTa1XjiaZ5Jf3+Ubj7WKiyvP9/dgz1L0bu2bYEjliw==", - "dependencies": { - "@aws-sdk/client-sso": "3.716.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/token-providers": "3.714.0", - "@aws-sdk/types": "3.714.0", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.758.0.tgz", + "integrity": "sha512-x0FYJqcOLUCv8GLLFDYMXRAQKGjoM+L0BG4BiHYZRDf24yQWFCAZsCQAYKo6XZYh2qznbsW6f//qpyJ5b0QVKQ==", + "dependencies": { + "@aws-sdk/client-sso": "3.758.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/token-providers": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.716.0.tgz", - "integrity": "sha512-vzgpWKs2gGXZGdbMKRFrMW4PqEFWkGvwWH2T7ZwQv9m+8lQ7P4Dk2uimqu0f37HZAbpn8HFMqRh4CaySjU354A==", - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/property-provider": "^3.1.11", - "@smithy/types": "^3.7.2", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.758.0.tgz", + "integrity": "sha512-XGguXhBqiCXMXRxcfCAVPlMbm3VyJTou79r/3mxWddHWF0XbhaQiBIbUz6vobVTD25YQRbWSmSch7VA8kI5Lrw==", + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sts": "^3.716.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.714.0.tgz", - "integrity": "sha512-I/xSOskiseJJ8i183Z522BgqbgYzLKP7jGcg2Qeib/IWoG2IP+9DH8pwqagKaPAycyswtnoKBJiiFXY43n0CkA==", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.734.0.tgz", + "integrity": "sha512-etC7G18aF7KdZguW27GE/wpbrNmYLVT755EsFc8kXpZj8D6AFKxc7OuveinJmiy0bYXAMspJUWsF6CrGpOw6CQ==", "dev": true, "dependencies": { - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-arn-parser": "3.693.0", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-config-provider": "^3.0.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-arn-parser": "3.723.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.714.0.tgz", - "integrity": "sha512-rlzsXdG8Lzo4Qpl35ZnpOBAWlzvDHpP9++0AXoUwAJA0QmMm7auIRmgxJuNj91VwT9h15ZU6xjU4S7fJl4W0+w==", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.734.0.tgz", + "integrity": "sha512-P38/v1l6HjuB2aFUewt7ueAW5IvKkFcv5dalPtbMGRhLeyivBOHwbCyuRKgVs7z7ClTpu9EaViEGki2jEQqEsQ==", "dev": true, "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.717.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.717.0.tgz", - "integrity": "sha512-a5kY5r7/7bDZZlOQQGWOR1ulQewdtNexdW1Ex5DD0FLKlFY7RD0va24hxQ6BP7mWHol+Dx4pj6UQ8ahk0ap1tw==", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.758.0.tgz", + "integrity": "sha512-o8Rk71S08YTKLoSobucjnbj97OCGaXgpEDNKXpXaavUM5xLNoHCLSUPRCiEN86Ivqxg1n17Y2nSRhfbsveOXXA==", "dev": true, "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-stream": "^3.3.2", - "@smithy/util-utf8": "^3.0.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.714.0.tgz", - "integrity": "sha512-6l68kjNrh5QC8FGX3I3geBDavWN5Tg1RLHJ2HLA8ByGBtJyCwnz3hEkKfaxn0bBx0hF9DzbfjEOUF6cDqy2Kjg==", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz", + "integrity": "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==", "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.714.0.tgz", - "integrity": "sha512-MX7M+V+FblujKck3fyuzePVIAy9530gY719IiSxV6uN1qLHl7VDJxNblpF/KpXakD6rOg8OpvtmqsXj9aBMftw==", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.734.0.tgz", + "integrity": "sha512-EJEIXwCQhto/cBfHdm3ZOeLxd2NlJD+X2F+ZTOxzokuhBtY0IONfC/91hOo5tWQweerojwshSMHRCKzRv1tlwg==", "dev": true, "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/types": "^3.7.2", + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.714.0.tgz", - "integrity": "sha512-RkqHlMvQWUaRklU1bMfUuBvdWwxgUtEqpADaHXlGVj3vtEY2UgBjy+57CveC4MByqKIunNvVHBBbjrGVtwY7Lg==", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.734.0.tgz", + "integrity": "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==", "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/types": "^3.7.2", + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.714.0.tgz", - "integrity": "sha512-AVU5ixnh93nqtsfgNc284oXsXaadyHGPHpql/jwgaaqQfEXjS/1/j3j9E/vpacfTTz2Vzo7hAOjnvrOXSEVDaA==", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz", + "integrity": "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==", "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.716.0.tgz", - "integrity": "sha512-Qzz5OfRA/5brqfvq+JHTInwS1EuJ1+tC6qMtwKWJN3czMnVJVdnnsPTf+G5IM/1yYaGEIjY8rC1ExQLcc8ApFQ==", - "dev": true, - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-arn-parser": "3.693.0", - "@smithy/core": "^2.5.5", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/protocol-http": "^4.1.8", - "@smithy/signature-v4": "^4.2.4", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-stream": "^3.3.2", - "@smithy/util-utf8": "^3.0.0", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.758.0.tgz", + "integrity": "sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A==", + "dev": true, + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-arn-parser": "3.723.0", + "@smithy/core": "^3.1.5", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.714.0.tgz", - "integrity": "sha512-RkK8REAVwNUQmYbIDRw8eYbMJ8F1Rw4C9mlME4BBMhFlelGcD3ErU2ce24moQbDxBjNwHNESmIqgmdQk93CDCQ==", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.734.0.tgz", + "integrity": "sha512-d4yd1RrPW/sspEXizq2NSOUivnheac6LPeLSLnaeTbBG9g1KqIqvCzP1TfXEqv2CrWfHEsWtJpX7oyjySSPvDQ==", "dev": true, "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/types": "^3.7.2", + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.716.0.tgz", - "integrity": "sha512-FpAtT6nNKrYdkDZndutEraiRMf+TgDzAGvniqRtZ/YTPA+gIsWrsn+TwMKINR81lFC3nQfb9deS5CFtxd021Ew==", - "dependencies": { - "@aws-sdk/core": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@aws-sdk/util-endpoints": "3.714.0", - "@smithy/core": "^2.5.5", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.758.0.tgz", + "integrity": "sha512-iNyehQXtQlj69JCgfaOssgZD4HeYGOwxcaKeG6F+40cwBjTAi0+Ph1yfDwqk2qiBPIRWJ/9l2LodZbxiBqgrwg==", + "dependencies": { + "@aws-sdk/core": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@smithy/core": "^3.1.5", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.758.0.tgz", + "integrity": "sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg==", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.758.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.743.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.758.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.5", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-retry": "^4.0.7", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.7", + "@smithy/util-defaults-mode-node": "^4.0.7", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.714.0.tgz", - "integrity": "sha512-HJzsQxgMOAzZrbf/YIqEx30or4tZK1oNAk6Wm6xecUQx+23JXIaePRu1YFUOLBBERQ4QBPpISFurZWBMZ5ibAw==", - "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.734.0.tgz", + "integrity": "sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.716.0.tgz", - "integrity": "sha512-k0goWotZKKz+kV6Ln0qeAMSeSVi4NipuIIz5R8A0uCF2zBK4CXWdZR7KeaIoLBhJwQnHj1UU7E+2MK74KIUBzA==", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.758.0.tgz", + "integrity": "sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw==", "dev": true, "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/protocol-http": "^4.1.8", - "@smithy/signature-v4": "^4.2.4", - "@smithy/types": "^3.7.2", + "@aws-sdk/middleware-sdk-s3": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.714.0.tgz", - "integrity": "sha512-vKN064aLE3kl+Zl16Ony3jltHnMddMBT7JRkP1L+lLywhA0PcAKxpdvComul/sTBWnbnwLnaS5NsDUhcWySH8A==", - "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.758.0.tgz", + "integrity": "sha512-ckptN1tNrIfQUaGWm/ayW1ddG+imbKN7HHhjFdS4VfItsP0QQOB0+Ov+tpgb4MoNR4JaUghMIVStjIeHN2ks1w==", + "dependencies": { + "@aws-sdk/nested-clients": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-sso-oidc": "^3.714.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/types": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.714.0.tgz", - "integrity": "sha512-ZjpP2gYbSFlxxaUDa1Il5AVvfggvUPbjzzB/l3q0gIE5Thd6xKW+yzEpt2mLZ5s5UaYSABZbF94g8NUOF4CVGA==", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.734.0.tgz", + "integrity": "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==", "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.693.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.693.0.tgz", - "integrity": "sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ==", + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.723.0.tgz", + "integrity": "sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==", "dev": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.714.0.tgz", - "integrity": "sha512-Xv+Z2lhe7w7ZZRsgBwBMZgGTVmS+dkkj2S13uNHAx9lhB5ovM8PhK5G/j28xYf6vIibeuHkRAbb7/ozdZIGR+A==", + "version": "3.743.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.743.0.tgz", + "integrity": "sha512-sN1l559zrixeh5x+pttrnd0A3+r34r0tmPkJ/eaaMaAzXqsmKU/xYre9K3FNnsSS1J1k4PEfk/nHDTVUgFYjnw==", "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/types": "^3.7.2", - "@smithy/util-endpoints": "^2.1.7", + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.535.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.535.0.tgz", - "integrity": "sha512-PHJ3SL6d2jpcgbqdgiPxkXpu7Drc2PYViwxSIqvvMKhDwzSB1W3mMvtpzwKM4IE7zLFodZo0GKjJ9AsoXndXhA==", + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.723.0.tgz", + "integrity": "sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.714.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.714.0.tgz", - "integrity": "sha512-OdJJ03cP9/MgIVToPJPCPUImbpZzTcwdIgbXC0tUQPJhbD7b7cB4LdnkhNHko+MptpOrCq4CPY/33EpOjRdofw==", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.734.0.tgz", + "integrity": "sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==", "dependencies": { - "@aws-sdk/types": "3.714.0", - "@smithy/types": "^3.7.2", + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.716.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.716.0.tgz", - "integrity": "sha512-3PqaXmQbxrtHKAsPCdp7kn5FrQktj8j3YyuNsqFZ8rWZeEQ88GWlsvE61PTsr2peYCKzpFqYVddef2x1axHU0w==", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.716.0", - "@aws-sdk/types": "3.714.0", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", + "version": "3.758.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.758.0.tgz", + "integrity": "sha512-A5EZw85V6WhoKMV2hbuFRvb9NPlxEErb4HPO6/SPXYY4QrjprIzScHxikqcWv1w4J3apB1wto9LPU3IMsYtfrw==", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.758.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" }, "peerDependencies": { "aws-crt": ">=1.0.0" @@ -1736,16 +1132,16 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.709.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.709.0.tgz", - "integrity": "sha512-2GPCwlNxeHspoK/Mc8nbk9cBOkSpp3j2SJUQmFnyQK6V/pR6II2oPRyZkMomug1Rc10hqlBHByMecq4zhV2uUw==", + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.734.0.tgz", + "integrity": "sha512-Zrjxi5qwGEcUsJ0ru7fRtW74WcTS0rbLcehoFB+rN1GRi2hbLcFaYs4PwVA5diLeAJH0gszv3x4Hr/S87MfbKQ==", "dev": true, "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/abort-controller": { @@ -1773,10 +1169,9 @@ } }, "node_modules/@azure/core-client": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", - "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", - "license": "MIT", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.3.tgz", + "integrity": "sha512-/wGw8fJ4mdpJ1Cum7s1S+VQyXt1ihwKLzfabS1O/RDADnmzVc01dHn44qD0BvGH6KlZNzOMW95tEpKqhkCChPA==", "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.4.0", @@ -1791,14 +1186,14 @@ } }, "node_modules/@azure/core-rest-pipeline": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.17.0.tgz", - "integrity": "sha512-62Vv8nC+uPId3j86XJ0WI+sBf0jlqTqPUFCBNrGtlaUeQUIXWV/D8GE5A1d+Qx8H7OQojn2WguC8kChD6v0shA==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.19.1.tgz", + "integrity": "sha512-zHeoI3NCs53lLBbWNzQycjnYKsA1CVKlnzSNuSFcUDwBp8HHVObePxrM7HaX+Ha5Ks639H7chNC9HOaIhNS03w==", "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.8.0", "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.9.0", + "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", @@ -1809,10 +1204,9 @@ } }, "node_modules/@azure/core-tracing": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.1.2.tgz", - "integrity": "sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==", - "license": "MIT", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", + "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", "dependencies": { "tslib": "^2.6.2" }, @@ -1833,9 +1227,9 @@ } }, "node_modules/@azure/identity": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.5.0.tgz", - "integrity": "sha512-EknvVmtBuSIic47xkOqyNabAme0RYTw52BTMz8eBgU1ysTyMrD1uOoM+JdS0J/4Yfp98IBT3osqq3BfwSaNaGQ==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.8.0.tgz", + "integrity": "sha512-l9ALUGHtFB/JfsqmA+9iYAp2a+cCwdNO/cyIr2y7nJLJsz1aae6qVP8XxT7Kbudg0IQRSIMXj0+iivFdbD1xPA==", "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.9.0", @@ -1844,11 +1238,11 @@ "@azure/core-tracing": "^1.0.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^3.26.1", - "@azure/msal-node": "^2.15.0", + "@azure/msal-browser": "^4.2.0", + "@azure/msal-node": "^3.2.3", "events": "^3.0.0", "jws": "^4.0.0", - "open": "^8.0.0", + "open": "^10.1.0", "stoppable": "^1.1.0", "tslib": "^2.2.0" }, @@ -1856,37 +1250,10 @@ "node": ">=18.0.0" } }, - "node_modules/@azure/identity/node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@azure/identity/node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@azure/logger": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.3.tgz", - "integrity": "sha512-J8/cIKNQB1Fc9fuYqBVnrppiUtW+5WWJPCj/tAokC5LdSTwkWWttN+jsRgw9BLYD7JDBx7PceiqOBxJJ1tQz3Q==", - "license": "MIT", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", + "integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==", "dependencies": { "tslib": "^2.6.2" }, @@ -1895,30 +1262,30 @@ } }, "node_modules/@azure/msal-browser": { - "version": "3.26.1", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.26.1.tgz", - "integrity": "sha512-y78sr9g61aCAH9fcLO1um+oHFXc1/5Ap88RIsUSuzkm0BHzFnN+PXGaQeuM1h5Qf5dTnWNOd6JqkskkMPAhh7Q==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.7.0.tgz", + "integrity": "sha512-H4AIPhIQVe1qW4+BJaitqod6UGQiXE3juj7q2ZBsOPjuZicQaqcbnBp2gCroF/icS0+TJ9rGuyCBJbjlAqVOGA==", "dependencies": { - "@azure/msal-common": "14.15.0" + "@azure/msal-common": "15.2.1" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "14.15.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.15.0.tgz", - "integrity": "sha512-ImAQHxmpMneJ/4S8BRFhjt1MZ3bppmpRPYYNyzeQPeFN288YKbb8TmmISQEbtfkQ1BPASvYZU5doIZOPBAqENQ==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.2.1.tgz", + "integrity": "sha512-eZHtYE5OHDN0o2NahCENkczQ6ffGc0MoUSAI3hpwGpZBHJXaEQMMZPWtIx86da2L9w7uT+Tr/xgJbGwIkvTZTQ==", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-node": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.15.0.tgz", - "integrity": "sha512-gVPW8YLz92ZeCibQH2QUw96odJoiM3k/ZPH3f2HxptozmH6+OnyyvKXo/Egg39HAM230akarQKHf0W74UHlh0Q==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.3.0.tgz", + "integrity": "sha512-ulsT3EHF1RQ29X55cxBLgKsIKWni9JdbUqG7sipGVP4uhWcBpmm/vhKOMH340+27Acm9+kHGnN/5XmQ5LrIDgA==", "dependencies": { - "@azure/msal-common": "14.15.0", + "@azure/msal-common": "15.2.1", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, @@ -1935,11 +1302,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dependencies": { - "@babel/highlight": "^7.24.2", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -1947,28 +1315,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", - "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", - "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.4", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.4", - "@babel/parser": "^7.24.4", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -1992,9 +1360,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.1.tgz", - "integrity": "sha512-d5guuzMlPeDfZIbpQ8+g1NaCNuAGBBGNECh0HVqz1sjOeVLh2CEaifuOysCH18URW6R7pqXINvf5PaR/dC6jLQ==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.26.10.tgz", + "integrity": "sha512-QsfQZr4AiLpKqn7fz+j7SN+f43z2DZCgGyYbNJ2vJOqKfG4E6MZer1+jqGZqKJaxq/gdO2DC/nUu45+pOL5p2Q==", "dev": true, "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", @@ -2006,7 +1374,7 @@ }, "peerDependencies": { "@babel/core": "^7.11.0", - "eslint": "^7.5.0 || ^8.0.0" + "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" } }, "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { @@ -2028,38 +1396,28 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", - "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", + "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", "dependencies": { - "@babel/types": "^7.24.0", + "@babel/parser": "^7.26.10", + "@babel/types": "^7.26.10", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -2075,58 +1433,26 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dependencies": { - "@babel/types": "^7.24.0" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -2136,78 +1462,57 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dependencies": { - "@babel/types": "^7.22.5" - }, + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", - "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", + "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", + "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", + "dev": true, + "peer": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.25.9", "chalk": "^2.4.2", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" @@ -2220,6 +1525,8 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -2231,6 +1538,8 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -2240,23 +1549,12 @@ "node": ">=4" } }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -2265,6 +1563,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, "engines": { "node": ">=4" } @@ -2273,6 +1573,8 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -2281,9 +1583,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", - "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "dependencies": { + "@babel/types": "^7.26.10" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -2324,6 +1629,34 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", @@ -2347,11 +1680,11 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", - "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -2426,6 +1759,20 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", @@ -2441,11 +1788,11 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", - "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -2455,10 +1802,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", - "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", - "license": "MIT", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2467,31 +1813,28 @@ } }, "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", - "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", - "dependencies": { - "@babel/code-frame": "^7.24.1", - "@babel/generator": "^7.24.1", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.1", - "@babel/types": "^7.24.0", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz", + "integrity": "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2508,13 +1851,12 @@ } }, "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -2553,6 +1895,116 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.2.tgz", + "integrity": "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz", + "integrity": "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@dabh/diagnostics": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", @@ -2575,24 +2027,27 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.0.tgz", + "integrity": "sha512-RoV8Xs9eNwiDvhv7M+xcL4PWyRyIXRY/FLp3buU4h1EYfdF7unWUy3dOjPqb3C7rMUewIcqwW850PgS8h1o1yg==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -2674,6 +2129,14 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@ewoudenberg/difflib": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@ewoudenberg/difflib/-/difflib-0.1.0.tgz", + "integrity": "sha512-OU5P5mJyD3OoWYMWY+yIgwvgNS9cFAU10f+DDuvtogcWQOoJIsQ4Hy2McSfUfhKjq8L0FuWVb4Rt7kgA+XK86A==", + "dependencies": { + "heap": ">= 0.2.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -2732,13 +2195,13 @@ "dev": true }, "node_modules/@inquirer/checkbox": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.4.tgz", - "integrity": "sha512-fYAKCAcGNMdfjL6hZTRUwkIByQ8EIZCXKrIQZH7XjADnN/xvRUhj8UdBbpC4zoUzvChhkSC/zRKaP/tDs3dZpg==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.3.tgz", + "integrity": "sha512-KU1MGwf24iABJjGESxhyj+/rlQYSRoCfcuHDEHXfZ1DENmbuSRfyrUb+LLjHoee5TNOFKwaFxDXc5/zRwJUPMQ==", "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", + "@inquirer/core": "^10.1.8", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.5", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, @@ -2747,136 +2210,93 @@ }, "peerDependencies": { "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/checkbox/node_modules/@inquirer/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", - "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", + "node_modules/@inquirer/confirm": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.7.tgz", + "integrity": "sha512-Xrfbrw9eSiHb+GsesO8TQIeHSMTP0xyvTCeeYevgZ4sKW+iz9w/47bgfG9b0niQm+xaLY2EWPBINUPldLwvYiw==", "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.1.8", + "@inquirer/type": "^3.0.5" }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/checkbox/node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", "engines": { "node": ">=18" }, "peerDependencies": { "@types/node": ">=18" - } - }, - "node_modules/@inquirer/checkbox/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@inquirer/checkbox/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/confirm": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz", - "integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==", - "dev": true, - "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/type": "^1.5.3" }, - "engines": { - "node": ">=18" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/@inquirer/core": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", - "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", - "dev": true, + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz", + "integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==", "dependencies": { - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.5.5", - "@types/wrap-ansi": "^3.0.0", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.5", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", + "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/core/node_modules/@inquirer/type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", - "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", - "dev": true, + "node_modules/@inquirer/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "mute-stream": "^1.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=18" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@inquirer/core/node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, + "node_modules/@inquirer/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=7.0.0" } }, - "node_modules/@inquirer/core/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "node_modules/@inquirer/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/@inquirer/core/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -2887,12 +2307,12 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.1.tgz", - "integrity": "sha512-xn9aDaiP6nFa432i68JCaL302FyL6y/6EG97nAtfIPnWZ+mWPgCMLGc4XZ2QQMsZtu9q3Jd5AzBPjXh10aX9kA==", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.8.tgz", + "integrity": "sha512-UkGKbMFlQw5k4ZLjDwEi5z8NIVlP/3DAlLHta0o0pSsdpPThNmPtUL8mvGCHUaQtR+QrxR9yRYNWgKMsHkfIUA==", "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/type": "^3.0.2", + "@inquirer/core": "^10.1.8", + "@inquirer/type": "^3.0.5", "external-editor": "^3.1.0" }, "engines": { @@ -2900,1557 +2320,509 @@ }, "peerDependencies": { "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/editor/node_modules/@inquirer/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", - "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", + "node_modules/@inquirer/expand": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.10.tgz", + "integrity": "sha512-leyBouGJ77ggv51Jb/OJmLGGnU2HYc13MZ2iiPNLwe2VgFgZPVqsrRWSa1RAHKyazjOyvSNKLD1B2K7A/iWi1g==", "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", + "@inquirer/core": "^10.1.8", + "@inquirer/type": "^3.0.5", "yoctocolors-cjs": "^2.1.2" }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/editor/node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", "engines": { "node": ">=18" }, "peerDependencies": { "@types/node": ">=18" - } - }, - "node_modules/@inquirer/editor/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/editor/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, + "node_modules/@inquirer/figures": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.11.tgz", + "integrity": "sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==", "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@inquirer/expand": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.4.tgz", - "integrity": "sha512-GYocr+BPyxKPxQ4UZyNMqZFSGKScSUc0Vk17II3J+0bDcgGsQm0KYQNooN1Q5iBfXsy3x/VWmHGh20QnzsaHwg==", + "node_modules/@inquirer/input": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.7.tgz", + "integrity": "sha512-rCQAipJNA14UTH84df/z4jDJ9LZ54H6zzuCAi7WZ0qVqx3CSqLjfXAMd5cpISIxbiHVJCPRB81gZksq6CZsqDg==", "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/type": "^3.0.2", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.1.8", + "@inquirer/type": "^3.0.5" }, "engines": { "node": ">=18" }, "peerDependencies": { "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/expand/node_modules/@inquirer/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", - "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", + "node_modules/@inquirer/number": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.10.tgz", + "integrity": "sha512-GLsdnxzNefjCJUmWyjaAuNklHgDpCTL4RMllAVhVvAzBwRW9g38eZ5tWgzo1lirtSDTpsh593hqXVhxvdrjfwA==", "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.1.8", + "@inquirer/type": "^3.0.5" }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/expand/node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", "engines": { "node": ">=18" }, "peerDependencies": { "@types/node": ">=18" - } - }, - "node_modules/@inquirer/expand/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/expand/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/@inquirer/password": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.10.tgz", + "integrity": "sha512-JC538ujqeYKkFqLoWZ0ILBteIUO2yajBMVEUZSxjl9x6fiEQtM+I5Rca7M2D8edMDbyHLnXifGH1hJZdh8V5rA==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@inquirer/core": "^10.1.8", + "@inquirer/type": "^3.0.5", + "ansi-escapes": "^4.3.2" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/figures": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.9.tgz", - "integrity": "sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==", + "node_modules/@inquirer/prompts": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.3.tgz", + "integrity": "sha512-QS1AQgJ113iE/nmym03yKZKHvGjVWwkGZT3B1yKrrMG0bJKQg1jUkntFP8aPd2FUQzu/nga7QU2eDpzIP5it0Q==", + "dependencies": { + "@inquirer/checkbox": "^4.1.3", + "@inquirer/confirm": "^5.1.7", + "@inquirer/editor": "^4.2.8", + "@inquirer/expand": "^4.0.10", + "@inquirer/input": "^4.1.7", + "@inquirer/number": "^3.0.10", + "@inquirer/password": "^4.0.10", + "@inquirer/rawlist": "^4.0.10", + "@inquirer/search": "^3.0.10", + "@inquirer/select": "^4.0.10" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/input": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.2.6.tgz", - "integrity": "sha512-32l4FxgY54O2YXVK6SHyC8gWZaemFBPHiMoKmJMqtwuicjHYF0meZKrTNPfHSOoxUzb6XVSICnXw0wKtsg7nKg==", - "dev": true, + "node_modules/@inquirer/rawlist": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.10.tgz", + "integrity": "sha512-vOQbQkmhaCsF2bUmjoyRSZJBz77UnIF/F3ZS2LMgwbgyaG2WgwKHh0WKNj0APDB72WDbZijhW5nObQbk+TnbcA==", "dependencies": { - "@inquirer/core": "^9.0.7", - "@inquirer/type": "^1.5.1" + "@inquirer/core": "^10.1.8", + "@inquirer/type": "^3.0.5", + "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/number": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.4.tgz", - "integrity": "sha512-DX7a6IXRPU0j8kr2ovf+QaaDiIf+zEKaZVzCWdLOTk7XigqSXvoh4cul7x68xp54WTQrgSnW7P1WBJDbyY3GhA==", + "node_modules/@inquirer/search": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.10.tgz", + "integrity": "sha512-EAVKAz6P1LajZOdoL+R+XC3HJYSU261fbJzO4fCkJJ7UPFcm+nP+gzC+DDZWsb2WK9PQvKsnaKiNKsY8B6dBWQ==", "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/type": "^3.0.2" + "@inquirer/core": "^10.1.8", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.5", + "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" }, "peerDependencies": { "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/number/node_modules/@inquirer/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", - "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", + "node_modules/@inquirer/select": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.10.tgz", + "integrity": "sha512-Tg8S9nESnCfISu5tCZSuXpXq0wHuDVimj7xyHstABgR34zcJnLdq/VbjB2mdZvNAMAehYBnNzSjxB06UE8LLAA==", "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", + "@inquirer/core": "^10.1.8", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.5", "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/number/node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", + "node_modules/@inquirer/type": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.5.tgz", + "integrity": "sha512-ZJpeIYYueOz/i/ONzrfof8g89kNdO2hjGuvULROo3O8rlB2CRtSseE5KeirnyE4t/thAn/EwvS/vuQeJCn+NZg==", "engines": { "node": ">=18" }, "peerDependencies": { "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@inquirer/number/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@inquirer/number/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@inquirer/password": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.4.tgz", - "integrity": "sha512-wiliQOWdjM8FnBmdIHtQV2Ca3S1+tMBUerhyjkRCv1g+4jSvEweGu9GCcvVEgKDhTBT15nrxvk5/bVrGUqSs1w==", - "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2" + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "engines": { - "node": ">=18" + "node": ">=12" }, - "peerDependencies": { - "@types/node": ">=18" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@inquirer/password/node_modules/@inquirer/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", - "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@inquirer/password/node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=18" + "node": ">=12" }, - "peerDependencies": { - "@types/node": ">=18" + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@inquirer/password/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, "engines": { - "node": ">=14" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@inquirer/password/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/@inquirer/prompts": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.2.1.tgz", - "integrity": "sha512-v2JSGri6/HXSfoGIwuKEn8sNCQK6nsB2BNpy2lSX6QH9bsECrMv93QHnj5+f+1ZWpF/VNioIV2B/PDox8EvGuQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dependencies": { - "@inquirer/checkbox": "^4.0.4", - "@inquirer/confirm": "^5.1.1", - "@inquirer/editor": "^4.2.1", - "@inquirer/expand": "^4.0.4", - "@inquirer/input": "^4.1.1", - "@inquirer/number": "^3.0.4", - "@inquirer/password": "^4.0.4", - "@inquirer/rawlist": "^4.0.4", - "@inquirer/search": "^3.0.4", - "@inquirer/select": "^4.0.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" + "sprintf-js": "~1.0.2" } }, - "node_modules/@inquirer/prompts/node_modules/@inquirer/confirm": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.1.tgz", - "integrity": "sha512-vVLSbGci+IKQvDOtzpPTCOiEJCNidHcAq9JYVoWTW0svb5FiwSLotkM+JXNXejfjnzVYV9n0DTBythl9+XgTxg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/type": "^3.0.2" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=18" + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "peerDependencies": { - "@types/node": ">=18" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@inquirer/prompts/node_modules/@inquirer/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", - "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/@inquirer/prompts/node_modules/@inquirer/input": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.1.tgz", - "integrity": "sha512-nAXAHQndZcXB+7CyjIW3XuQZZHbQQ0q8LX6miY6bqAWwDzNa9JUioDBYrFmOUNIsuF08o1WT/m2gbBXvBhYVxg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/type": "^3.0.2" + "p-try": "^2.0.0" }, "engines": { - "node": ">=18" + "node": ">=6" }, - "peerDependencies": { - "@types/node": ">=18" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@inquirer/prompts/node_modules/@inquirer/select": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.4.tgz", - "integrity": "sha512-ZzYLuLoUzTIW9EJm++jBpRiTshGqS3Q1o5qOEQqgzaBlmdsjQr6pA4TUNkwu6OBYgM2mIRbCz6mUhFDfl/GF+w==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" + "node": ">=8" } }, - "node_modules/@inquirer/prompts/node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" + "node": ">=8" } }, - "node_modules/@inquirer/prompts/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, - "node_modules/@inquirer/prompts/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@inquirer/rawlist": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.4.tgz", - "integrity": "sha512-IsVN2EZdNHsmFdKWx9HaXb8T/s3FlR/U1QPt9dwbSyPtjFbMTlW9CRFvnn0bm/QIsrMRD2oMZqrQpSWPQVbXXg==", + "node_modules/@jest/console/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/type": "^3.0.2", - "yoctocolors-cjs": "^2.1.2" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=18" + "node": ">=8" }, - "peerDependencies": { - "@types/node": ">=18" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@inquirer/rawlist/node_modules/@inquirer/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", - "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/rawlist/node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, - "node_modules/@inquirer/rawlist/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@inquirer/rawlist/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/search": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.4.tgz", - "integrity": "sha512-tSkJk2SDmC2MEdTIjknXWmCnmPr5owTs9/xjfa14ol1Oh95n6xW7SYn5fiPk4/vrJPys0ggSWiISdPze4LTa7A==", - "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, - "node_modules/@inquirer/search/node_modules/@inquirer/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", - "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", - "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/search/node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, - "node_modules/@inquirer/search/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@inquirer/search/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/select": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", - "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", - "dev": true, - "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.3", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/type": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", - "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", - "dev": true, - "dependencies": { - "mute-stream": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/type/node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@mdi/js": { - "version": "7.4.47", - "resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz", - "integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==" - }, - "node_modules/@microsoft/microsoft-graph-client": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-3.0.7.tgz", - "integrity": "sha512-/AazAV/F+HK4LIywF9C+NYHcJo038zEnWkteilcxC1FM/uK/4NVGDKGrxx7nNq1ybspAroRKT4I1FHfxQzxkUw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependenciesMeta": { - "@azure/identity": { - "optional": true - }, - "@azure/msal-browser": { - "optional": true - }, - "buffer": { - "optional": true - }, - "stream-browserify": { - "optional": true - } - } - }, - "node_modules/@microsoft/microsoft-graph-types": { - "version": "2.40.0", - "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-2.40.0.tgz", - "integrity": "sha512-1fcPVrB/NkbNcGNfCy+Cgnvwxt6/sbIEEFgZHFBJ670zYLegENYJF8qMo7x3LqBjWX2/Eneq5BVVRCLTmlJN+g==", - "license": "MIT" - }, - "node_modules/@mitre/emass_client": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@mitre/emass_client/-/emass_client-3.10.0.tgz", - "integrity": "sha512-4QlS5FoREGwV31010VCteo59enbPCNEUBemaEzHEIqUoTwi/S2Sk98TUKdMYOMClP4JtNj6YTPqtdq4v6CHnBQ==", - "dependencies": { - "axios": "^0.21.4" - } - }, - "node_modules/@mitre/emass_client/node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, - "node_modules/@mitre/hdf-converters": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@mitre/hdf-converters/-/hdf-converters-2.11.1.tgz", - "integrity": "sha512-JO86IpmBm/KC3skmn6Dau2GjeaPSz9LxF4NkWAF4DTnPkJ2XODPNWEAM3FJeu7SHycC1g8VmjRn5UPeUXNvrNA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-config-service": "^3.95.0", - "@e965/xlsx": "^0.20.0", - "@mdi/js": "^7.0.96", - "@microsoft/microsoft-graph-types": "^2.40.0", - "@mitre/jsonix": "^3.0.7", - "@smithy/node-http-handler": "^3.0.0", - "@types/csv2json": "^1.4.2", - "@types/ms": "^0.7.31", - "@types/mustache": "^4.1.2", - "@types/papaparse": "^5.3.2", - "@types/revalidator": "^0.3.12", - "@types/triple-beam": "^1.3.2", - "@types/validator": "^13.12.0", - "@types/xml2js": "^0.4.9", - "axios": "^1.3.5", - "compare-versions": "^6.0.0", - "csv2json": "^2.0.2", - "fast-xml-parser": "^4.2.0", - "html-entities": "^2.3.2", - "htmlparser2": "^9.1.0", - "inspecjs": "^2.11.0", - "lodash": "^4.17.21", - "moment": "^2.29.1", - "ms": "^2.1.3", - "mustache": "^4.2.0", - "papaparse": "^5.3.1", - "revalidator": "^0.3.1", - "run-script-os": "^1.1.6", - "semver": "^7.6.0", - "tailwindcss": "^3.3.3", - "tw-elements": "^1.0.0-beta2", - "validator": "^13.12.0", - "winston": "^3.6.0", - "xml-formatter": "^3.6.2", - "xml-parser-xo": "^4.1.1", - "xml2js": "^0.6.0", - "yaml": "^2.1.1" - } - }, - "node_modules/@mitre/hdf-converters/node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" - } - }, - "node_modules/@mitre/heimdall-lite": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/@mitre/heimdall-lite/-/heimdall-lite-2.11.1.tgz", - "integrity": "sha512-9/V1WKwsmv/jrDprctQ9hD+j+nzS6KwWW/LK+67fkDBCo/et6j69Br6mtpMHQU9TO4qTRcpUwzU4zzR19bClAA==", - "license": "Apache-2.0", - "dependencies": { - "express": "^4.17.1" - }, - "bin": { - "heimdall-lite": "src/server.js" - }, - "engines": { - "node": ">=22" - } - }, - "node_modules/@mitre/inspec-objects": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@mitre/inspec-objects/-/inspec-objects-1.0.1.tgz", - "integrity": "sha512-WnE263XPXUVIlxwon150ZvgqjPAT8/eXAisLSbBDBwCbUvRWR3lFJZecIyeIrg2z9DczRHIGaznOqHM+T82AOA==", - "dependencies": { - "@types/flat": "^5.0.2", - "@types/he": "^1.1.2", - "@types/json-diff": "^0.7.0", - "@types/jstoxml": "^2.0.2", - "@types/lodash": "^4.14.178", - "@types/mustache": "^4.2.0", - "@types/pretty": "^2.0.1", - "fast-xml-parser": "^3.1.19", - "flat": "^5.0.2", - "he": "^1.2.0", - "htmlparser2": "^7.2.0", - "inspecjs": "^2.6.6", - "jest": "^28.1.1", - "json-diff": "^0.9.0", - "jstoxml": "^3.2.3", - "lodash": "^4.17.21", - "mustache": "^4.2.0", - "pretty": "^2.0.0", - "ts-jest": "^28.0.4", - "typescript": "^4.5.5", - "winston": "^3.8.1", - "yaml": "^1.10.2" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/core": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", - "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", - "dependencies": { - "@jest/console": "^28.1.3", - "@jest/reporters": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^28.1.3", - "jest-config": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-resolve-dependencies": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "jest-watcher": "^28.1.3", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", - "dependencies": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", - "dependencies": { - "expect": "^28.1.3", - "jest-snapshot": "^28.1.3" + "node": ">=10" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/expect-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", - "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", + "node_modules/@jest/console/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "jest-get-type": "^28.0.2" + "color-name": "~1.1.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=7.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dependencies": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } + "node_modules/@jest/console/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/globals": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", - "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "node_modules/@jest/console/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/types": "^28.1.3" + "has-flag": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=8" } }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/reporters": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", - "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", + "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", + "ci-info": "^3.2.0", "exit": "^0.1.2", - "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^9.0.1" + "strip-ansi": "^6.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" @@ -4461,192 +2833,21 @@ } } }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/source-map": { - "version": "28.1.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", - "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.13", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dependencies": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/test-sequencer": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", - "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", - "dependencies": { - "@jest/test-result": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "slash": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/transform": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", - "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" - }, - "node_modules/@mitre/inspec-objects/node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@mitre/inspec-objects/node_modules/babel-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", - "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", - "dependencies": { - "@jest/transform": "^28.1.3", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^28.1.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/babel-plugin-jest-hoist": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", - "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/babel-preset-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", - "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", - "dependencies": { - "babel-plugin-jest-hoist": "^28.1.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/chalk": { + "node_modules/@jest/core/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -4661,32 +2862,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@mitre/inspec-objects/node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/ci-info": { + "node_modules/@jest/core/node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", @@ -4700,758 +2876,645 @@ "node": ">=8" } }, - "node_modules/@mitre/inspec-objects/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "node_modules/@mitre/inspec-objects/node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" - }, - "node_modules/@mitre/inspec-objects/node_modules/diff-sequences": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "node_modules/@jest/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/core/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dependencies": { - "domelementtype": "^2.2.0" + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@mitre/inspec-objects/node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "node_modules/@jest/reporters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=0.12" + "node": ">=8" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@mitre/inspec-objects/node_modules/expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "@jest/expect-utils": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@mitre/inspec-objects/node_modules/fast-xml-parser": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz", - "integrity": "sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==", + "node_modules/@jest/reporters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "strnum": "^1.0.4" - }, - "bin": { - "xml2js": "cli.js" + "color-name": "~1.1.4" }, - "funding": { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "bin": { - "flat": "cli.js" - } + "node_modules/@jest/reporters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/@mitre/inspec-objects/node_modules/htmlparser2": { + "node_modules/@jest/reporters/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", - "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.2", - "domutils": "^2.8.0", - "entities": "^3.0.1" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/@mitre/inspec-objects/node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", - "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dependencies": { - "@jest/core": "^28.1.3", - "@jest/types": "^28.1.3", - "import-local": "^3.0.2", - "jest-cli": "^28.1.3" - }, - "bin": { - "jest": "bin/jest.js" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-changed-files": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", - "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dependencies": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-circus": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", - "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dependencies": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "p-limit": "^3.1.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-cli": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", - "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dependencies": { - "@jest/core": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-config": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", - "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^28.1.3", - "@jest/types": "^28.1.3", - "babel-jest": "^28.1.3", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-circus": "^28.1.3", - "jest-environment-node": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^28.1.3", + "pirates": "^4.0.4", "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "write-file-atomic": "^4.0.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-diff": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", - "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "node_modules/@jest/transform/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^28.1.1", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" + "color-convert": "^2.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-docblock": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", - "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "detect-newline": "^3.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-each": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", - "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", + "node_modules/@jest/transform/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "jest-util": "^28.1.3", - "pretty-format": "^28.1.3" + "color-name": "~1.1.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=7.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-environment-node": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", - "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "node_modules/@jest/transform/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" + "has-flag": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=8" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-haste-map": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", - "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dependencies": { - "@jest/types": "^28.1.3", - "@types/graceful-fs": "^4.1.3", + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-leak-detector": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", - "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" + "color-convert": "^2.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-matcher-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", - "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "color-name": "~1.1.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=7.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*" + "has-flag": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=8" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-resolve": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", - "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=6.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-resolve-dependencies": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", - "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", - "dependencies": { - "jest-regex-util": "^28.0.2", - "jest-snapshot": "^28.1.3" - }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=6.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-runner": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", - "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", - "dependencies": { - "@jest/console": "^28.1.3", - "@jest/environment": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "graceful-fs": "^4.2.9", - "jest-docblock": "^28.1.1", - "jest-environment-node": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-leak-detector": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-resolve": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-util": "^28.1.3", - "jest-watcher": "^28.1.3", - "jest-worker": "^28.1.3", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=6.0.0" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-runtime": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", - "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dependencies": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/globals": "^28.1.3", - "@jest/source-map": "^28.1.2", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-snapshot": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", - "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "node_modules/@mdi/js": { + "version": "7.4.47", + "resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz", + "integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==" + }, + "node_modules/@microsoft/microsoft-graph-client": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-client/-/microsoft-graph-client-3.0.7.tgz", + "integrity": "sha512-/AazAV/F+HK4LIywF9C+NYHcJo038zEnWkteilcxC1FM/uK/4NVGDKGrxx7nNq1ybspAroRKT4I1FHfxQzxkUw==", "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-haste-map": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "natural-compare": "^1.4.0", - "pretty-format": "^28.1.3", - "semver": "^7.3.5" + "@babel/runtime": "^7.12.5", + "tslib": "^2.2.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=12.0.0" + }, + "peerDependenciesMeta": { + "@azure/identity": { + "optional": true + }, + "@azure/msal-browser": { + "optional": true + }, + "buffer": { + "optional": true + }, + "stream-browserify": { + "optional": true + } } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "node_modules/@microsoft/microsoft-graph-types": { + "version": "2.40.0", + "resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-2.40.0.tgz", + "integrity": "sha512-1fcPVrB/NkbNcGNfCy+Cgnvwxt6/sbIEEFgZHFBJ670zYLegENYJF8qMo7x3LqBjWX2/Eneq5BVVRCLTmlJN+g==" + }, + "node_modules/@mitre/emass_client": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@mitre/emass_client/-/emass_client-3.22.0.tgz", + "integrity": "sha512-FB8pfqDe9KG+ZrJe239cfUJRNKwhvGn/j98wpMHdVPWy8K6/DVdV90ZNpu4oPSL/XZ3NES78UScMR9+9XiyMXQ==", "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "axios": "^1.6.1" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-validate": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", - "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "node_modules/@mitre/hdf-converters": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@mitre/hdf-converters/-/hdf-converters-2.11.5.tgz", + "integrity": "sha512-H2dzvV2l9n2oed9ki1zYAyjK0ZPMeOdCJ1AdiiiCHS1hqC2WL2VLyDChHOX80OVcG0Srx+3NEF58nXZvSBiujw==", "dependencies": { - "@jest/types": "^28.1.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "leven": "^3.1.0", - "pretty-format": "^28.1.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "@aws-sdk/client-config-service": "^3.95.0", + "@e965/xlsx": "^0.20.0", + "@mdi/js": "^7.0.96", + "@microsoft/microsoft-graph-types": "^2.40.0", + "@mitre/jsonix": "^3.0.7", + "@smithy/node-http-handler": "^4.0.0", + "@types/csv2json": "^1.4.2", + "@types/ms": "^0.7.31", + "@types/mustache": "^4.1.2", + "@types/papaparse": "^5.3.2", + "@types/revalidator": "^0.3.12", + "@types/triple-beam": "^1.3.2", + "@types/validator": "^13.12.0", + "@types/xml2js": "^0.4.9", + "axios": "^1.3.5", + "compare-versions": "^6.0.0", + "csv2json": "^2.0.2", + "fast-xml-parser": "4.5.3", + "html-entities": "^2.3.2", + "htmlparser2": "^10.0.0", + "inspecjs": "^2.11.0", + "lodash": "^4.17.21", + "moment": "^2.29.1", + "ms": "^2.1.3", + "mustache": "^4.2.0", + "papaparse": "^5.3.1", + "revalidator": "^0.3.1", + "run-script-os": "^1.1.6", + "semver": "^7.6.0", + "tailwindcss": "^4.0.6", + "tw-elements": "^1.0.0-beta2", + "validator": "^13.12.0", + "winston": "^3.6.0", + "xml-formatter": "^3.6.2", + "xml-parser-xo": "^4.1.1", + "xml2js": "^0.6.0", + "yaml": "^2.1.1" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "node_modules/@mitre/hdf-converters/node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "dependencies": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" + "strnum": "^1.1.1" }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "bin": { + "fxparser": "src/cli/cli.js" } }, - "node_modules/@mitre/inspec-objects/node_modules/jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } + "node_modules/@mitre/hdf-converters/node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] }, - "node_modules/@mitre/inspec-objects/node_modules/pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "node_modules/@mitre/heimdall-lite": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/@mitre/heimdall-lite/-/heimdall-lite-2.11.5.tgz", + "integrity": "sha512-Q9H45094Qq6s4JwceWMCOTJn9wEw+LMlg84K3z5b+w2StEhJtsh6zWAG8I/12OkTWJMeGKbHaoBQnCJPOhkiew==", "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "express": "^4.17.1" + }, + "bin": { + "heimdall-lite": "src/server.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + "node": ">=22" } }, - "node_modules/@mitre/inspec-objects/node_modules/resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "engines": { - "node": ">=10" + "node_modules/@mitre/inspec-objects": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@mitre/inspec-objects/-/inspec-objects-2.0.1.tgz", + "integrity": "sha512-mIv/hOg89CXTAS+8wvT8tFL4jM9tYa0oUednBpOdktVvpTNxqeOUGyeDMGSPX1wbmpAlvAtRhwGKtjiFzCLcYQ==", + "dependencies": { + "@types/flat": "5.0.2", + "@types/he": "^1.1.2", + "@types/json-diff": "^1.0.0", + "@types/jstoxml": "^2.0.2", + "@types/lodash": "^4.14.178", + "@types/mustache": "^4.2.0", + "@types/pretty": "^2.0.1", + "fast-xml-parser": "^4.5.1", + "flat": "5.0.2", + "he": "^1.2.0", + "htmlparser2": "^10.0.0", + "inspecjs": "^2.6.6", + "json-diff": "^1.0.6", + "jstoxml": "^5.0.2", + "lodash": "^4.17.21", + "mustache": "^4.2.0", + "pretty": "^2.0.0", + "winston": "^3.8.1", + "yaml": "^2.3.1" } }, - "node_modules/@mitre/inspec-objects/node_modules/ts-jest": { - "version": "28.0.8", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", - "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", + "node_modules/@mitre/inspec-objects/node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^28.0.0", - "json5": "^2.2.1", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" + "strnum": "^1.1.1" }, "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^28.0.0", - "babel-jest": "^28.0.0", - "jest": "^28.0.0", - "typescript": ">=4.3" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } + "fxparser": "src/cli/cli.js" } }, - "node_modules/@mitre/inspec-objects/node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "node_modules/@mitre/inspec-objects/node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/@mitre/inspec-objects/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" + "flat": "cli.js" } }, - "node_modules/@mitre/inspec-objects/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } + "node_modules/@mitre/inspec-objects/node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] }, "node_modules/@mitre/jsonix": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@mitre/jsonix/-/jsonix-3.0.7.tgz", - "integrity": "sha512-8aDNL2v0b2BqaOi+3Hf9/EuNkUMVzFPYOusLx9MY/Ky9LLz2D5BxgGL8+h5mnYpTiHyFknGm4Wr/MFmz7oHLYw==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@mitre/jsonix/-/jsonix-3.0.9.tgz", + "integrity": "sha512-2WawetgXqqSg2tw85v5jMMbyEdXgaqSOsn889iKTP26UDW/p1MdrtIoo5tzYsxp5q00qupEZz4OC/cqH9dQTiA==", "dependencies": { - "@xmldom/xmldom": "^0.8.2", - "amdefine": "^0.0.4", + "@xmldom/xmldom": "^0.9.7", + "amdefine": "^1.0.1", "xmlhttprequest": "^1.8.0" } }, @@ -5519,12 +3582,12 @@ } }, "node_modules/@oclif/core": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.2.0.tgz", - "integrity": "sha512-ETM2N/GL7W37Kv1Afv1j1Gh77CynS2ubEPP+p+MnjUXEjghNe7+bKAWhPkHnBuFAVFAqdv0qMpUAjxKLbsmbJw==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.2.10.tgz", + "integrity": "sha512-fAqcXgqkUm4v5FYy7qWP4w1HaOlVSVJveah+yVTo5Nm5kTiXhmD5mQQ7+knGeBaStyrtQy6WardoC2xSic9rlQ==", "dependencies": { "ansi-escapes": "^4.3.2", - "ansis": "^3.3.2", + "ansis": "^3.17.0", "clean-stack": "^3.0.1", "cli-spinners": "^2.9.2", "debug": "^4.4.0", @@ -5546,21 +3609,10 @@ "node": ">=18.0.0" } }, - "node_modules/@oclif/core/node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/@oclif/plugin-help": { - "version": "6.2.20", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.20.tgz", - "integrity": "sha512-2l4A/erCAdBWmJwb1LJ7TvSMbBUQbGhIzkdHZb5DMgFJC+Nwfeol5YojqRMeciyGkoqmWPBGENwr0LJgZp2skw==", + "version": "6.2.26", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.26.tgz", + "integrity": "sha512-5KdldxEizbV3RsHOddN4oMxrX/HL6z79S94tbxEHVZ/dJKDWzfyCpgC9axNYqwmBF2pFZkozl/l7t3hCGOdalw==", "dependencies": { "@oclif/core": "^4" }, @@ -5569,14 +3621,14 @@ } }, "node_modules/@oclif/plugin-not-found": { - "version": "3.2.33", - "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.33.tgz", - "integrity": "sha512-1RgvZ0J5KloU8TRemHxCr5MbVtr41ungnz8BBCPJn2yR5L+Eo2Lt+kpOyEeYAohjo4Tml1AHSmipUF4jKThwTw==", + "version": "3.2.45", + "resolved": "https://registry.npmjs.org/@oclif/plugin-not-found/-/plugin-not-found-3.2.45.tgz", + "integrity": "sha512-iDNYUpS6LPoPd3Tm5IqwmQC+bbNdFSJoCYdKK1T6VVjujbIXyoSK/QUv62Y91aoJJebE/tzagcwCs1P89m+m7g==", "dev": true, "dependencies": { - "@inquirer/prompts": "^7.2.1", + "@inquirer/prompts": "^7.3.2", "@oclif/core": "^4", - "ansis": "^3.5.2", + "ansis": "^3.17.0", "fast-levenshtein": "^3.0.0" }, "engines": { @@ -5584,18 +3636,18 @@ } }, "node_modules/@oclif/plugin-plugins": { - "version": "5.4.24", - "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-5.4.24.tgz", - "integrity": "sha512-Mzo0XNeD/5dshgLX9Ft7cpgSQKVQd7fw0PoaE/+58d7K/Lz00wxbo4zKjw19j65pqBfsKv7zVmg+jWxu3PZpgA==", + "version": "5.4.34", + "resolved": "https://registry.npmjs.org/@oclif/plugin-plugins/-/plugin-plugins-5.4.34.tgz", + "integrity": "sha512-19sX+tHyR6M24GHbnedqqtk6w9QBgFZoa1y4zPmHf6VYaBeBmMBaq2dsLsdG0zv8LnWxaqguocICoxZTrV9f6A==", "dependencies": { - "@oclif/core": "^4.0.34", - "ansis": "^3.5.2", + "@oclif/core": "^4.2.6", + "ansis": "^3.14.0", "debug": "^4.4.0", "npm": "^10.9.2", "npm-package-arg": "^11.0.3", "npm-run-path": "^5.3.0", "object-treeify": "^4.0.1", - "semver": "^7.6.3", + "semver": "^7.7.1", "validate-npm-package-name": "^5.0.1", "which": "^4.0.0", "yarn": "^1.22.22" @@ -5604,170 +3656,41 @@ "node": ">=18.0.0" } }, - "node_modules/@oclif/plugin-plugins/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "engines": { - "node": ">=16" - } - }, - "node_modules/@oclif/plugin-plugins/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@oclif/plugin-plugins/node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@oclif/plugin-plugins/node_modules/object-treeify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-4.0.1.tgz", - "integrity": "sha512-Y6tg5rHfsefSkfKujv2SwHulInROy/rCL5F4w0QOWxut8AnxYxf0YmNhTh95Zfyxpsudo66uqkux0ACFnyMSgQ==", - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/@oclif/plugin-plugins/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/@oclif/plugin-plugins/node_modules/yarn": { - "version": "1.22.22", - "resolved": "https://registry.npmjs.org/yarn/-/yarn-1.22.22.tgz", - "integrity": "sha512-prL3kGtyG7o9Z9Sv8IPfBNrWTDmXB4Qbes8A9rEzt6wkJV8mUvoirjU0Mp3GGAU06Y0XQyA3/2/RQFVuK7MTfg==", - "hasInstallScript": true, - "license": "BSD-2-Clause", - "bin": { - "yarn": "bin/yarn.js", - "yarnpkg": "bin/yarn.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/@oclif/plugin-version": { - "version": "2.2.19", - "resolved": "https://registry.npmjs.org/@oclif/plugin-version/-/plugin-version-2.2.19.tgz", - "integrity": "sha512-AdYn3RDb1SBHkhr8YS7j0rMTm2Bz+BxIKFsb/v5Hm8JABWwAK3n/+EJML/7TAc9dYS8oi6+qOiOXL78Cybh8ww==", + "version": "2.2.25", + "resolved": "https://registry.npmjs.org/@oclif/plugin-version/-/plugin-version-2.2.25.tgz", + "integrity": "sha512-5TbteyArV5MKbtXErenyW1OR6SIf1yjMSKIr522PbtucbUkANBf0lxuriBCuNPv7BowiC/Uq5QED/xyPm737wg==", "dependencies": { "@oclif/core": "^4", - "ansis": "^3.5.2" + "ansis": "^3.16.0" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@oclif/plugin-warn-if-update-available": { - "version": "3.1.29", - "resolved": "https://registry.npmjs.org/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-3.1.29.tgz", - "integrity": "sha512-NmD6hcyBquo9TV26tnYnsbyR69VzaeMC3kqks0YT2947CSJS7smONMxpkjU2P4kLTH4Tn8/n5w/sjoegNe1jdw==", + "version": "3.1.37", + "resolved": "https://registry.npmjs.org/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-3.1.37.tgz", + "integrity": "sha512-MBQxCGKOxOH0MuXztm6ju1Od/ApDE6+IOQnVQAgwkkmralVmPzE10M3YSUw3/X8TiNpGL50Ja+sC3uNtK78swA==", "dependencies": { "@oclif/core": "^4", - "ansis": "^3.5.2", + "ansis": "^3.17.0", "debug": "^4.4.0", "http-call": "^5.2.2", "lodash": "^4.17.21", - "registry-auth-token": "^5.0.3" + "registry-auth-token": "^5.1.0" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@oclif/plugin-warn-if-update-available/node_modules/http-call": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/http-call/-/http-call-5.3.0.tgz", - "integrity": "sha512-ahwimsC23ICE4kPl9xTBjKB4inbRaeLyZeRunC/1Jy/Z6X8tv22MEAjK+KBOMSVLaqXPTTmd8638waVIKLGx2w==", - "license": "ISC", - "dependencies": { - "content-type": "^1.0.4", - "debug": "^4.1.1", - "is-retry-allowed": "^1.1.0", - "is-stream": "^2.0.0", - "parse-json": "^4.0.0", - "tunnel-agent": "^0.6.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@oclif/plugin-warn-if-update-available/node_modules/http-call/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@oclif/plugin-warn-if-update-available/node_modules/is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@oclif/plugin-warn-if-update-available/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", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "license": "MIT" - }, - "node_modules/@oclif/plugin-warn-if-update-available/node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/@oclif/test": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@oclif/test/-/test-4.1.5.tgz", - "integrity": "sha512-w4hVfP+Lsz7toaSnj/E3cFH4Y/nHt1k14cCREQXe/nIqnZW7J6xGnjYEmqU9BPk7m0Lsl52AqJ2hGvLarGhfLw==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@oclif/test/-/test-4.1.12.tgz", + "integrity": "sha512-EExENu6DOjAGJPx2VRFI2ZNR3dBMep5Q+pBhk+4f2OdKxEqrY8/Fr7lXBn9jq7n9w28r8sn5LZ8P4tn3oVd20w==", "dev": true, "dependencies": { - "ansis": "^3.5.2", + "ansis": "^3.17.0", "debug": "^4.4.0" }, "engines": { @@ -5795,7 +3718,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", - "license": "MIT", "engines": { "node": ">=12.22.0" } @@ -5804,7 +3726,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "license": "MIT", "dependencies": { "graceful-fs": "4.2.10" }, @@ -5815,14 +3736,12 @@ "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "license": "ISC" + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "node_modules/@pnpm/npm-conf": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", - "license": "MIT", "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", @@ -5866,6 +3785,14 @@ "type-detect": "4.0.8" } }, + "node_modules/@sinonjs/commons/node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, "node_modules/@sinonjs/fake-timers": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", @@ -5875,289 +3802,303 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.9.tgz", - "integrity": "sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz", + "integrity": "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==", "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/chunked-blob-reader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-4.0.0.tgz", - "integrity": "sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", + "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", "dev": true, "dependencies": { "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@smithy/chunked-blob-reader-native": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.1.tgz", - "integrity": "sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", + "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", "dev": true, "dependencies": { - "@smithy/util-base64": "^3.0.0", + "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@smithy/config-resolver": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.13.tgz", - "integrity": "sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.1.tgz", + "integrity": "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==", "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/core": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.5.tgz", - "integrity": "sha512-G8G/sDDhXA7o0bOvkc7bgai6POuSld/+XhNnWAbpQTpLv2OZPvyqQ58tLPPlz0bSNsXktldDDREIv1LczFeNEw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-stream": "^3.3.2", - "@smithy/util-utf8": "^3.0.0", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.5.tgz", + "integrity": "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA==", + "dependencies": { + "@smithy/middleware-serde": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.1.2", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz", - "integrity": "sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz", + "integrity": "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==", "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/eventstream-codec": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.10.tgz", - "integrity": "sha512-323B8YckSbUH0nMIpXn7HZsAVKHYHFUODa8gG9cHo0ySvA1fr5iWaNT+iIL0UCqUzG6QPHA3BSsBtRQou4mMqQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.1.tgz", + "integrity": "sha512-Q2bCAAR6zXNVtJgifsU16ZjKGqdw/DyecKNgIgi7dlqw04fqDu0mnq+JmGphqheypVc64CYq3azSuCpAdFk2+A==", "dev": true, "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.7.2", - "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.14.tgz", - "integrity": "sha512-kbrt0vjOIihW3V7Cqj1SXQvAI5BR8SnyQYsandva0AOR307cXAc+IhPngxIPslxTLfxwDpNu0HzCAq6g42kCPg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.1.tgz", + "integrity": "sha512-HbIybmz5rhNg+zxKiyVAnvdM3vkzjE6ccrJ620iPL8IXcJEntd3hnBl+ktMwIy12Te/kyrSbUb8UCdnUT4QEdA==", "dev": true, "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.13", - "@smithy/types": "^3.7.2", + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.11.tgz", - "integrity": "sha512-P2pnEp4n75O+QHjyO7cbw/vsw5l93K/8EWyjNCAAybYwUmj3M+hjSQZ9P5TVdUgEG08ueMAP5R4FkuSkElZ5tQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.0.1.tgz", + "integrity": "sha512-lSipaiq3rmHguHa3QFF4YcCM3VJOrY9oq2sow3qlhFY+nBSTF/nrO82MUQRPrxHQXA58J5G1UnU2WuJfi465BA==", "dev": true, "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.13.tgz", - "integrity": "sha512-zqy/9iwbj8Wysmvi7Lq7XFLeDgjRpTbCfwBhJa8WbrylTAHiAu6oQTwdY7iu2lxigbc9YYr9vPv5SzYny5tCXQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.1.tgz", + "integrity": "sha512-o4CoOI6oYGYJ4zXo34U8X9szDe3oGjmHgsMGiZM0j4vtNoT+h80TLnkUcrLZR3+E6HIxqW+G+9WHAVfl0GXK0Q==", "dev": true, "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.13", - "@smithy/types": "^3.7.2", + "@smithy/eventstream-serde-universal": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.13.tgz", - "integrity": "sha512-L1Ib66+gg9uTnqp/18Gz4MDpJPKRE44geOjOQ2SVc0eiaO5l255ADziATZgjQjqumC7yPtp1XnjHlF1srcwjKw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.1.tgz", + "integrity": "sha512-Z94uZp0tGJuxds3iEAZBqGU2QiaBHP4YytLUjwZWx+oUeohCsLyUm33yp4MMBmhkuPqSbQCXq5hDet6JGUgHWA==", "dev": true, "dependencies": { - "@smithy/eventstream-codec": "^3.1.10", - "@smithy/types": "^3.7.2", + "@smithy/eventstream-codec": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/fetch-http-handler": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.2.tgz", - "integrity": "sha512-R7rU7Ae3ItU4rC0c5mB2sP5mJNbCfoDc8I5XlYjIZnquyUwec7fEo78F6DA3SmgJgkU1qTMcZJuGblxZsl10ZA==", - "license": "Apache-2.0", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz", + "integrity": "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==", "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@smithy/hash-blob-browser": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.10.tgz", - "integrity": "sha512-elwslXOoNunmfS0fh55jHggyhccobFkexLYC1ZeZ1xP2BTSrcIBaHV2b4xUQOdctrSNOpMqOZH1r2XzWTEhyfA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.1.tgz", + "integrity": "sha512-rkFIrQOKZGS6i1D3gKJ8skJ0RlXqDvb1IyAphksaFOMzkn3v3I1eJ8m7OkLj0jf1McP63rcCEoLlkAn/HjcTRw==", "dev": true, "dependencies": { - "@smithy/chunked-blob-reader": "^4.0.0", - "@smithy/chunked-blob-reader-native": "^3.0.1", - "@smithy/types": "^3.7.2", + "@smithy/chunked-blob-reader": "^5.0.0", + "@smithy/chunked-blob-reader-native": "^4.0.0", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@smithy/hash-node": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.11.tgz", - "integrity": "sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.1.tgz", + "integrity": "sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==", "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", + "@smithy/types": "^4.1.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/hash-stream-node": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-3.1.10.tgz", - "integrity": "sha512-olomK/jZQ93OMayW1zfTHwcbwBdhcZOHsyWyiZ9h9IXvc1mCD/VuvzbLb3Gy/qNJwI4MANPLctTp2BucV2oU/Q==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.1.tgz", + "integrity": "sha512-U1rAE1fxmReCIr6D2o/4ROqAQX+GffZpyMt3d7njtGDr2pUNmAKRWa49gsNVhCh2vVAuf3wXzWwNr2YN8PAXIw==", "dev": true, "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-utf8": "^3.0.0", + "@smithy/types": "^4.1.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/invalid-dependency": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz", - "integrity": "sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz", + "integrity": "sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==", "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@smithy/is-array-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", - "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/md5-js": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.11.tgz", - "integrity": "sha512-3NM0L3i2Zm4bbgG6Ymi9NBcxXhryi3uE8fIfHJZIOfZVxOkGdjdgjR9A06SFIZCfnEIWKXZdm6Yq5/aPXFFhsQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.1.tgz", + "integrity": "sha512-HLZ647L27APi6zXkZlzSFZIjpo8po45YiyjMGJZM3gyDY8n7dPGdmxIIljLm4gPt/7rRvutLTTkYJpZVfG5r+A==", "dev": true, "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-utf8": "^3.0.0", + "@smithy/types": "^4.1.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz", - "integrity": "sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz", + "integrity": "sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==", "dependencies": { - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.6.tgz", - "integrity": "sha512-WAqzyulvvSKrT5c6VrQelgNVNNO7BlTQW9Z+s9tcG6G5CaBS1YBpPtT3VuhXLQbewSiGi7oXQROwpw26EG9PLQ==", - "dependencies": { - "@smithy/core": "^2.5.5", - "@smithy/middleware-serde": "^3.0.11", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", - "@smithy/url-parser": "^3.0.11", - "@smithy/util-middleware": "^3.0.11", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.6.tgz", + "integrity": "sha512-ftpmkTHIFqgaFugcjzLZv3kzPEFsBFSnq1JsIkr2mwFzCraZVhQk2gqN51OOeRxqhbPTkRFj39Qd2V91E/mQxg==", + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-serde": "^4.0.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.31", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.31.tgz", - "integrity": "sha512-yq9wawrJLYHAYFpChLujxRN4My+SiKXvZk9Ml/CvTdRSA8ew+hvuR5LT+mjSlSBv3c4XJrkN8CWegkBaeD0Vrg==", - "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/protocol-http": "^4.1.8", - "@smithy/service-error-classification": "^3.0.11", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-retry": "^3.0.11", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.7.tgz", + "integrity": "sha512-58j9XbUPLkqAcV1kHzVX/kAR16GT+j7DUZJqwzsxh1jtz7G82caZiGyyFgUvogVfNTg3TeAOIJepGc8TXF4AVQ==", + "dependencies": { + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-retry/node_modules/uuid": { @@ -6173,388 +4114,381 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz", - "integrity": "sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==", - "license": "Apache-2.0", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.2.tgz", + "integrity": "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==", "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-stack": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz", - "integrity": "sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz", + "integrity": "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==", "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/node-config-provider": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz", - "integrity": "sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz", + "integrity": "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==", "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/shared-ini-file-loader": "^3.1.12", - "@smithy/types": "^3.7.2", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/node-http-handler": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.3.tgz", - "integrity": "sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.3.tgz", + "integrity": "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA==", "dependencies": { - "@smithy/abort-controller": "^3.1.9", - "@smithy/protocol-http": "^4.1.8", - "@smithy/querystring-builder": "^3.0.11", - "@smithy/types": "^3.7.2", + "@smithy/abort-controller": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/property-provider": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.11.tgz", - "integrity": "sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.1.tgz", + "integrity": "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==", "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/protocol-http": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.8.tgz", - "integrity": "sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.1.tgz", + "integrity": "sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==", "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/querystring-builder": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz", - "integrity": "sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz", + "integrity": "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==", "dependencies": { - "@smithy/types": "^3.7.2", - "@smithy/util-uri-escape": "^3.0.0", + "@smithy/types": "^4.1.0", + "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/querystring-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz", - "integrity": "sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz", + "integrity": "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==", "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/service-error-classification": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz", - "integrity": "sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz", + "integrity": "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==", "dependencies": { - "@smithy/types": "^3.7.2" + "@smithy/types": "^4.1.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz", - "integrity": "sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz", + "integrity": "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==", "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/signature-v4": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.4.tgz", - "integrity": "sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.11", - "@smithy/util-uri-escape": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.1.tgz", + "integrity": "sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/smithy-client": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.5.1.tgz", - "integrity": "sha512-PmjskH4Os1Eh3rd5vSsa5uVelZ4DRu+N5CBEgb9AT96hQSJGWSEb6pGxKV/PtKQSIp9ft3+KvnT8ViMKaguzgA==", - "dependencies": { - "@smithy/core": "^2.5.5", - "@smithy/middleware-endpoint": "^3.2.6", - "@smithy/middleware-stack": "^3.0.11", - "@smithy/protocol-http": "^4.1.8", - "@smithy/types": "^3.7.2", - "@smithy/util-stream": "^3.3.2", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.6.tgz", + "integrity": "sha512-UYDolNg6h2O0L+cJjtgSyKKvEKCOa/8FHYJnBobyeoeWDmNpXjwOAtw16ezyeu1ETuuLEOZbrynK0ZY1Lx9Jbw==", + "dependencies": { + "@smithy/core": "^3.1.5", + "@smithy/middleware-endpoint": "^4.0.6", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.1.2", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/types": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.2.tgz", - "integrity": "sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.1.0.tgz", + "integrity": "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/url-parser": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.11.tgz", - "integrity": "sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.1.tgz", + "integrity": "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==", "dependencies": { - "@smithy/querystring-parser": "^3.0.11", - "@smithy/types": "^3.7.2", + "@smithy/querystring-parser": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@smithy/util-base64": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", - "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-body-length-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", - "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", "dependencies": { "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@smithy/util-body-length-node": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", - "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-buffer-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", - "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", "dependencies": { - "@smithy/is-array-buffer": "^3.0.0", + "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-config-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", - "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.31", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.31.tgz", - "integrity": "sha512-eO+zkbqrPnmsagqzrmF7IJrCoU2wTQXWVYxMPqA9Oue55kw9WEvhyuw2XQzTVTCRcYsg6KgmV3YYhLlWQJfK1A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.7.tgz", + "integrity": "sha512-CZgDDrYHLv0RUElOsmZtAnp1pIjwDVCSuZWOPhIOBvG36RDfX1Q9+6lS61xBf+qqvHoqRjHxgINeQz47cYFC2Q==", "dependencies": { - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", "bowser": "^2.11.0", "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.31", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.31.tgz", - "integrity": "sha512-0/nJfpSpbGZOs6qs42wCe2TdjobbnnD4a3YUUlvTXSQqLy4qa63luDaV04hGvqSHP7wQ7/WGehbvHkDhMZd1MQ==", - "dependencies": { - "@smithy/config-resolver": "^3.0.13", - "@smithy/credential-provider-imds": "^3.2.8", - "@smithy/node-config-provider": "^3.1.12", - "@smithy/property-provider": "^3.1.11", - "@smithy/smithy-client": "^3.5.1", - "@smithy/types": "^3.7.2", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.7.tgz", + "integrity": "sha512-79fQW3hnfCdrfIi1soPbK3zmooRFnLpSx3Vxi6nUlqaaQeC5dm8plt4OTNDNqEEEDkvKghZSaoti684dQFVrGQ==", + "dependencies": { + "@smithy/config-resolver": "^4.0.1", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/smithy-client": "^4.1.6", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-endpoints": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz", - "integrity": "sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==", - "license": "Apache-2.0", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz", + "integrity": "sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==", "dependencies": { - "@smithy/node-config-provider": "^3.1.12", - "@smithy/types": "^3.7.2", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-hex-encoding": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", - "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-middleware": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.11.tgz", - "integrity": "sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.1.tgz", + "integrity": "sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==", "dependencies": { - "@smithy/types": "^3.7.2", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-retry": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.11.tgz", - "integrity": "sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==", - "license": "Apache-2.0", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.1.tgz", + "integrity": "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==", "dependencies": { - "@smithy/service-error-classification": "^3.0.11", - "@smithy/types": "^3.7.2", + "@smithy/service-error-classification": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-stream": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.2.tgz", - "integrity": "sha512-sInAqdiVeisUGYAv/FrXpmJ0b4WTFmciTRqzhb7wVuem9BHvhIG7tpiYHLDWrl2stOokNZpTTGqz3mzB2qFwXg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^4.1.2", - "@smithy/node-http-handler": "^3.3.2", - "@smithy/types": "^3.7.2", - "@smithy/util-base64": "^3.0.0", - "@smithy/util-buffer-from": "^3.0.0", - "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-utf8": "^3.0.0", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.1.2.tgz", + "integrity": "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw==", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.3", + "@smithy/types": "^4.1.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-uri-escape": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", - "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", "dependencies": { "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", - "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", "dependencies": { - "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@smithy/util-waiter": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.2.0.tgz", - "integrity": "sha512-PpjSboaDUE6yl+1qlg3Si57++e84oXdWGbuFUSAciXsVfEZJJJupR2Nb0QuXHiunt2vGR+1PTizOMvnUPaG2Qg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.2.tgz", + "integrity": "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==", "dev": true, "dependencies": { - "@smithy/abort-controller": "^3.1.9", - "@smithy/types": "^3.7.2", + "@smithy/abort-controller": "^4.0.1", + "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@szmarczak/http-timer": { @@ -6619,9 +4553,9 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dependencies": { "@babel/types": "^7.20.7" } @@ -6636,9 +4570,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.16", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz", - "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==" + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", + "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==" }, "node_modules/@types/connect": { "version": "3.4.38", @@ -6676,9 +4610,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.0.tgz", - "integrity": "sha512-AbXMTZGt40T+KON9/Fdxx0B2WK5hsgxcfXJLr5bFpZ7b4JCex2WyQPTEKdXqfHiY5nKKBScZ7yCoO6Pvgxfvnw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -6687,9 +4621,9 @@ } }, "node_modules/@types/flat": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@types/flat/-/flat-5.0.5.tgz", - "integrity": "sha512-nPLljZQKSnac53KDUDzuzdRfGI0TDb5qPrb+SrQyN3MtdQrOnGsKniHN1iYZsJEBIVQve94Y6gNz22sgISZq+Q==" + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-3zsplnP2djeps5P9OyarTxwRpMLoe5Ash8aL9iprw0JxB+FAHjY+ifn4yZUuW4/9hqtnmor6uvjSRzJhiVbrEQ==" }, "node_modules/@types/fs-extra": { "version": "11.0.4", @@ -6777,9 +4711,9 @@ } }, "node_modules/@types/json-diff": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@types/json-diff/-/json-diff-0.7.0.tgz", - "integrity": "sha512-20IJqupGHywtIaE6fS30iygh3dVqVdzmsnrYn/VFuRaQKxLx/RGH5K9hhCfctVxgN7KzJlnD7gYFAOrNwiCgtA==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/json-diff/-/json-diff-1.0.3.tgz", + "integrity": "sha512-Qvxm8fpRMv/1zZR3sQWImeRK2mBYJji20xF51Fq9Gt//Ed18u0x6/FNLogLS1xhfUWTEmDyqveJqn95ltB6Kvw==" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -6787,13 +4721,6 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true, - "optional": true - }, "node_modules/@types/jsonfile": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", @@ -6808,9 +4735,9 @@ "integrity": "sha512-XHUMp2t8CfJUsKt8qjlspZWlkVeveFg2eF/kRV8CKiNQ+Fd3mJkfexIDXtZEnG/4aJZRCVNNn46zNddSVIUN0w==" }, "node_modules/@types/lodash": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", - "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==" + "version": "4.17.16", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz", + "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==" }, "node_modules/@types/mime": { "version": "1.3.5", @@ -6851,9 +4778,9 @@ } }, "node_modules/@types/node": { - "version": "22.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.4.tgz", - "integrity": "sha512-99l6wv4HEzBQhvaU/UGoeBoCK61SCROQaCCGyQSgX2tEQ3rKkNZ2S7CEWnS/4s1LV+8ODdK21UeyR1fHP2mXug==", + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", "dependencies": { "undici-types": "~6.20.0" } @@ -6870,18 +4797,13 @@ "integrity": "sha512-C+fMdS2gK+rNUc5iAjH/d4z+oNayctBrpF5G9DO4SpqB78wG/Tkp2RfI8/6aKf/R+LOaARcQaxQ3G/6UmAg8GQ==" }, "node_modules/@types/papaparse": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.14.tgz", - "integrity": "sha512-LxJ4iEFcpqc6METwp9f6BV6VVc43m6MfH0VqFosHvrUgfXiFe6ww7R3itkOQ+TCK6Y+Iv/+RnnvtRZnkc5Kc9g==", + "version": "5.3.15", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.15.tgz", + "integrity": "sha512-JHe6vF6x/8Z85nCX4yFdDslN11d+1pr12E526X8WAfhadOeaOTx5AuIkvDKIBopfvlzpzkdMx4YyvSKCM9oqtw==", "dependencies": { "@types/node": "*" } }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" - }, "node_modules/@types/pretty": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/pretty/-/pretty-2.0.3.tgz", @@ -6902,9 +4824,9 @@ } }, "node_modules/@types/qs": { - "version": "6.9.16", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", - "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==" + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==" }, "node_modules/@types/range-parser": { "version": "1.2.7", @@ -6962,9 +4884,9 @@ "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==" }, "node_modules/@types/validator": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.0.tgz", - "integrity": "sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==" + "version": "13.12.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.2.tgz", + "integrity": "sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==" }, "node_modules/@types/wrap-ansi": { "version": "3.0.0", @@ -6981,89 +4903,33 @@ } }, "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@types/yargs-parser": "*" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7071,6 +4937,15 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@typescript-eslint/experimental-utils": { @@ -7203,16 +5078,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.1.tgz", - "integrity": "sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.7.1", - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/typescript-estree": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4" }, "engines": { @@ -7232,14 +5107,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz", - "integrity": "sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1" + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -7276,7 +5150,7 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "node_modules/@typescript-eslint/types": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", @@ -7289,7 +5163,7 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "node_modules/@typescript-eslint/typescript-estree": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", @@ -7317,66 +5191,6 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.1.tgz", - "integrity": "sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==", - "dev": true, - "peer": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.1.tgz", - "integrity": "sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@typescript-eslint/utils": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", @@ -7399,65 +5213,7 @@ "eslint": "^8.56.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "node_modules/@typescript-eslint/visitor-keys": { "version": "7.18.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", @@ -7474,36 +5230,18 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.1.tgz", - "integrity": "sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/types": "7.7.1", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, "node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", + "integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==", "engines": { - "node": ">=10.0.0" + "node": ">=14.6" } }, "node_modules/abbrev": { @@ -7532,9 +5270,9 @@ "integrity": "sha512-I1aXdUeHUVjWlGjSbBd8NaME/CQU942H2tHMEuZ5et/xb52mDhikIebAi9ufEoAe8lwzHrbCl4R5euCZ8K/wfA==" }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "bin": { "acorn": "bin/acorn" }, @@ -7552,9 +5290,12 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { "node": ">=0.4.0" } @@ -7568,13 +5309,9 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "engines": { "node": ">= 14" } @@ -7595,9 +5332,9 @@ } }, "node_modules/amdefine": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.0.4.tgz", - "integrity": "sha512-+9uNlaqm8kZ0bYDuhFt1mqNoLM2I4AsSeB+6dddNiSfmRlJRq38IUuNtUD4+xOzOoPltOHzSvnlSgscMfpnDDg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", "engines": { "node": ">=0.4.2" } @@ -7634,25 +5371,22 @@ } }, "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/ansis": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.5.2.tgz", - "integrity": "sha512-5uGcUZRbORJeEppVdWfZOSybTMz+Ou+84HepgK081Yk5+pPMMzWf/XGxiAT6bfBqCghRB4MwBtYn0CHqINRVag==", + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", "engines": { - "node": ">=16" + "node": ">=14" } }, "node_modules/any-promise": { @@ -7681,9 +5415,9 @@ } }, "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" }, "node_modules/argparse": { "version": "2.0.1", @@ -7720,9 +5454,9 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, "node_modules/async-csv": { "version": "2.1.3", @@ -7747,10 +5481,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.9", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", - "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", - "license": "MIT", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", + "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -7777,6 +5510,20 @@ "@babel/core": "^7.8.0" } }, + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/babel-jest/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7792,6 +5539,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/babel-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/babel-jest/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7856,22 +5619,25 @@ } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0" @@ -7975,9 +5741,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "funding": [ { "type": "opencollective", @@ -7993,10 +5759,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -8035,8 +5801,7 @@ "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, "node_modules/buffer-from": { "version": "1.1.2", @@ -8104,16 +5869,25 @@ "node": ">=14.16" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -8157,9 +5931,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001666", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001666.tgz", - "integrity": "sha512-gD14ICmoV5ZZM1OdzPWmpx+q4GyefaK06zi8hmfHV5xe4/2nOQX3+Dw5o+fSqOws2xVwL9j+anOPFwHzdEdV4g==", + "version": "1.0.30001703", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz", + "integrity": "sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==", "funding": [ { "type": "opencollective", @@ -8215,14 +5989,6 @@ "node": ">=4" } }, - "node_modules/chai/node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "engines": { - "node": ">=4" - } - }, "node_modules/chalk": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", @@ -8292,15 +6058,9 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -8313,6 +6073,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -8329,9 +6092,9 @@ } }, "node_modules/ci-info": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", + "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", "dev": true, "funding": [ { @@ -8344,9 +6107,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==" }, "node_modules/clean-regexp": { "version": "1.0.0", @@ -8383,21 +6146,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-color": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.4.tgz", - "integrity": "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.64", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.15", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/cli-spinners": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", @@ -8444,21 +6192,27 @@ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==" }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "color-name": "1.1.3" } }, "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/color-string": { "version": "1.9.1", @@ -8491,28 +6245,6 @@ "text-hex": "1.0.x" } }, - "node_modules/colorspace/node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/colorspace/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/colorspace/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -8533,9 +6265,9 @@ } }, "node_modules/compare-versions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz", - "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==" + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==" }, "node_modules/concat-map": { "version": "0.0.1", @@ -8619,12 +6351,12 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/core-js-compat": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", - "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", + "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", "dev": true, "dependencies": { - "browserslist": "^4.23.3" + "browserslist": "^4.24.4" }, "funding": { "type": "opencollective", @@ -8667,6 +6399,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/create-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/create-jest/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8682,6 +6428,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/create-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/create-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/create-jest/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8711,6 +6473,25 @@ "node": ">= 8" } }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -8723,11 +6504,13 @@ } }, "node_modules/cssstyle": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", - "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.0.tgz", + "integrity": "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ==", + "dev": true, "dependencies": { - "rrweb-cssom": "^0.7.1" + "@asamuzakjp/css-color": "^3.1.1", + "rrweb-cssom": "^0.8.0" }, "engines": { "node": ">=18" @@ -8800,22 +6583,11 @@ "resolved": "https://registry.npmjs.org/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz", "integrity": "sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w==" }, - "node_modules/d": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", - "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", - "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" @@ -8853,9 +6625,10 @@ } }, "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "dev": true }, "node_modules/decompress-response": { "version": "6.0.0", @@ -8898,9 +6671,9 @@ } }, "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dependencies": { "type-detect": "^4.0.0" }, @@ -8957,22 +6730,6 @@ "node": ">=10" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -9040,9 +6797,9 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "engines": { "node": ">=0.3.1" } @@ -9055,17 +6812,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/difflib": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", - "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", - "dependencies": { - "heap": ">= 0.2.0" - }, - "engines": { - "node": "*" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -9107,6 +6853,17 @@ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", @@ -9133,9 +6890,9 @@ } }, "node_modules/domutils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.1.tgz", - "integrity": "sha512-xWXmuRnN9OMP6ptPd2+H0cCbcYBULa5YDTbMm/2lvkWvNA3O4wcW+GvzooqBuNM8yy6pl3VIAeJTUUWUbfI5Fw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -9159,7 +6916,6 @@ "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -9178,6 +6934,19 @@ "node": ">=0.4.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexify": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", @@ -9198,7 +6967,6 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" } @@ -9254,9 +7022,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.32", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.32.tgz", - "integrity": "sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==" + "version": "1.5.115", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.115.tgz", + "integrity": "sha512-MN1nahVHAQMOz6dz6bNZ7apgqc9InZy7Ja4DBEVCTdeiUcegbyOYE9bi/f2Z/z6ZxLi0RxLpyJ3EGe+4h3w73A==" }, "node_modules/emittery": { "version": "0.13.1", @@ -9280,9 +7048,9 @@ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -9310,9 +7078,9 @@ } }, "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", "engines": { "node": ">=0.12" }, @@ -9329,12 +7097,9 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -9347,52 +7112,29 @@ "node": ">= 0.4" } }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "hasInstallScript": true, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" + "es-errors": "^1.3.0" }, "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "node": ">= 0.4" } }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", - "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=0.12" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" + "node": ">= 0.4" } }, "node_modules/escalade": { @@ -9423,6 +7165,7 @@ "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -9493,6 +7236,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-1.0.3.tgz", "integrity": "sha512-TeJKXWBQ3uKMtzgz++UFNWpe1WCx8mfqRuzZy1LirREgRlVv656SkVG4gNZat5rRNIQgfDmTS+YebxK02kfylA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "dependencies": { "@typescript-eslint/eslint-plugin": "^4.31.2", @@ -9550,6 +7294,7 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "peer": true, "dependencies": { @@ -9565,6 +7310,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true, "peer": true }, @@ -9731,6 +7477,22 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/eslint-config-oclif-typescript/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/eslint-config-oclif-typescript/node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -9769,10 +7531,31 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/eslint-config-oclif-typescript/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, "node_modules/eslint-config-oclif-typescript/node_modules/eslint": { "version": "7.32.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "peer": true, "dependencies": { @@ -10265,9 +8048,9 @@ } }, "node_modules/eslint-plugin-unicorn/node_modules/globals": { - "version": "15.10.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.10.0.tgz", - "integrity": "sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==", + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, "engines": { "node": ">=18" @@ -10368,8 +8151,23 @@ "uri-js": "^4.2.2" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/eslint/node_modules/brace-expansion": { @@ -10398,6 +8196,24 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -10428,20 +8244,6 @@ "node": ">=8" } }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -10521,20 +8323,10 @@ "node": ">= 0.6" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", "engines": { "node": ">=0.8.x" } @@ -10569,6 +8361,22 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/execa/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -10607,7 +8415,6 @@ "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -10657,27 +8464,11 @@ "ms": "2.0.0" } }, - "node_modules/express/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dependencies": { - "type": "^2.7.2" - } - }, "node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -10719,15 +8510,15 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -10759,26 +8550,32 @@ } }, "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] }, "node_modules/fast-xml-parser": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", - "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.0.8.tgz", + "integrity": "sha512-qY8NiI5L8ff00F2giyICiJxSSKHO52tC36LJqx2JtvGyAd5ZfehC/l4iUVVHpmpIa6sM9N5mneSLHQG2INGoHA==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" } ], "dependencies": { - "strnum": "^1.0.5" + "strnum": "^2.0.5" }, "bin": { "fxparser": "src/cli/cli.js" @@ -10794,9 +8591,9 @@ } }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dependencies": { "reusify": "^1.0.4" } @@ -10881,14 +8678,6 @@ "ms": "2.0.0" } }, - "node_modules/finalhandler/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -10953,9 +8742,9 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, "node_modules/fn.name": { @@ -10964,9 +8753,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -10983,11 +8772,11 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -10997,24 +8786,14 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -11047,9 +8826,9 @@ } }, "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -11064,6 +8843,19 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -11079,9 +8871,9 @@ "dev": true }, "node_modules/fuse.js": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz", - "integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz", + "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==", "engines": { "node": ">=10" } @@ -11124,15 +8916,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -11149,6 +8946,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stdin": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", @@ -11173,9 +8982,9 @@ } }, "node_modules/git-hooks-list": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.1.0.tgz", - "integrity": "sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.2.0.tgz", + "integrity": "sha512-ZHG9a1gEhUMX1TvGrLdyWb9kDopCBbTnI8z4JgRMYxsijWipgjSEYoPWqBuIB0DnRnvqlQSEeVmzpeuPm7NdFQ==", "dev": true, "funding": { "url": "https://github.com/fisker/git-hooks-list?sponsor=1" @@ -11191,6 +9000,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -11265,6 +9075,11 @@ "node": ">=0.10.0" } }, + "node_modules/global-prefix/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, "node_modules/global-prefix/node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -11323,11 +9138,11 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11396,21 +9211,10 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -11418,10 +9222,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, "engines": { "node": ">= 0.4" }, @@ -11486,17 +9293,15 @@ } }, "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, "dependencies": { "whatwg-encoding": "^3.1.1" }, @@ -11542,23 +9347,28 @@ "entities": "^6.0.0" } }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", - "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, + "node_modules/http-call": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/http-call/-/http-call-5.3.0.tgz", + "integrity": "sha512-ahwimsC23ICE4kPl9xTBjKB4inbRaeLyZeRunC/1Jy/Z6X8tv22MEAjK+KBOMSVLaqXPTTmd8638waVIKLGx2w==", + "dependencies": { + "content-type": "^1.0.4", + "debug": "^4.1.1", + "is-retry-allowed": "^1.1.0", + "is-stream": "^2.0.0", + "parse-json": "^4.0.0", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -11578,7 +9388,6 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -11606,12 +9415,11 @@ "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==" }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "license": "MIT", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -11638,9 +9446,9 @@ } }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "engines": { "node": ">= 4" } @@ -11651,9 +9459,9 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "dependencies": { "parent-module": "^1.0.0", @@ -11667,9 +9475,9 @@ } }, "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -11704,167 +9512,64 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dependencies": { "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/inquirer": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.3.0.tgz", - "integrity": "sha512-3NixUXq+hM8ezj2wc7wC37b32/rHq1MwNZDYdvx+d6jokOD+r+i8Q4Pkylh9tISYP114A128LCX8RKhopC5RfQ==", - "dependencies": { - "@inquirer/core": "^10.1.2", - "@inquirer/prompts": "^7.2.1", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", - "mute-stream": "^2.0.0", - "run-async": "^3.0.0", - "rxjs": "^7.8.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, - "node_modules/inquirer-file-selector": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/inquirer-file-selector/-/inquirer-file-selector-0.6.1.tgz", - "integrity": "sha512-YllHPQDRL0k8tqWe3KqCscis7QF2ukqAZzDxE9SDuplXgveGm7qi9NxUFc7Em/7rk0OWFLEzUUCo8NSIOB+FBA==", - "dependencies": { - "@inquirer/core": "^10.1.0", - "@inquirer/figures": "^1.0.8", - "chalk": "^5.3.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/inquirer-file-selector/node_modules/@inquirer/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", - "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", - "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/inquirer-file-selector/node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - } - }, - "node_modules/inquirer-file-selector/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/inquirer-file-selector/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" + "wrappy": "1" } }, - "node_modules/inquirer/node_modules/@inquirer/core": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", - "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/inquirer": { + "version": "12.4.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.4.3.tgz", + "integrity": "sha512-p9+jcDKhFHKTunvpffCk7I9eKt8+NPNWO8hMSSoLPv5vahP5Vhr78qWzDtA+6FBWQtFTuLFUWmxTyhC6G2Xz/Q==", "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", + "@inquirer/core": "^10.1.8", + "@inquirer/prompts": "^7.3.3", + "@inquirer/type": "^3.0.5", "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "run-async": "^3.0.0", + "rxjs": "^7.8.2" }, - "engines": { - "node": ">=18" - } - }, - "node_modules/inquirer/node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", "engines": { "node": ">=18" }, "peerDependencies": { "@types/node": ">=18" - } - }, - "node_modules/inquirer/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "engines": { - "node": ">=14" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/inquirer/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/inquirer-file-selector": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inquirer-file-selector/-/inquirer-file-selector-0.6.2.tgz", + "integrity": "sha512-foxy2K6OcYsDNavvJ69WiuleduT8x+DChIe4IfrsijmYI216Dn3Hfr5jNT6jg4taYulN2D221GHCu21QmcM5Fw==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@inquirer/core": "^10.1.7", + "@inquirer/figures": "^1.0.10", + "chalk": "^5.4.1" }, "engines": { - "node": ">=8" + "node": ">=18" } }, "node_modules/inspecjs": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/inspecjs/-/inspecjs-2.11.0.tgz", - "integrity": "sha512-CR6cOXqJ9XAqZz3YoMRMK4s8j6psgRDV5YrlXtKXLpaiRdAfKVoWI5j6TFHj0TK+ykEQzuTr7mBgF9BzJiHBxg==", - "license": "Apache-2.0" + "integrity": "sha512-CR6cOXqJ9XAqZz3YoMRMK4s8j6psgRDV5YrlXtKXLpaiRdAfKVoWI5j6TFHj0TK+ykEQzuTr7mBgF9BzJiHBxg==" }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -11911,11 +9616,14 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -12027,23 +9735,30 @@ } }, "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" + "node_modules/is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "engines": { + "node": ">=0.10.0" + } }, "node_modules/is-stream": { "version": "2.0.1", @@ -12106,9 +9821,12 @@ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "engines": { + "node": ">=16" + } }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", @@ -12119,9 +9837,9 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", - "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -12183,15 +9901,12 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=14" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -12200,9 +9915,9 @@ } }, "node_modules/jake": { - "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -12216,6 +9931,20 @@ "node": ">=10" } }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jake/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -12240,6 +9969,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jake/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -12330,6 +10075,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-circus/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12345,6 +10104,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-circus/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-circus/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12388,6 +10163,20 @@ } } }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-cli/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12403,6 +10192,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-cli/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-cli/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12458,6 +10263,20 @@ } } }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-config/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12487,6 +10306,39 @@ "node": ">=8" } }, + "node_modules/jest-config/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/jest-config/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jest-config/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12512,6 +10364,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-diff/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12527,6 +10393,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-diff/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12564,6 +10446,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-each/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12579,6 +10475,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-each/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-each/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12664,6 +10576,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-matcher-utils/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12679,6 +10605,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-matcher-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-matcher-utils/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12709,6 +10651,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-message-util/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12724,6 +10680,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-message-util/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12803,6 +10775,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-resolve/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-resolve/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12818,6 +10804,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-resolve/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-resolve/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12860,6 +10862,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-runner/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12875,6 +10891,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-runner/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-runner/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12918,6 +10950,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-runtime/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12933,6 +10979,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-runtime/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-runtime/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12974,6 +11036,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-snapshot/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12989,6 +11065,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-snapshot/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-snapshot/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13016,6 +11108,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-util/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -13045,6 +11151,22 @@ "node": ">=8" } }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-util/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13072,6 +11194,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-validate/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -13098,6 +11234,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-validate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-validate/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13127,6 +11279,20 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-watcher/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/jest-watcher/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -13142,6 +11308,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-watcher/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/jest-watcher/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13168,23 +11350,23 @@ } }, "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "bin": { "jiti": "bin/jiti.js" } }, "node_modules/js-beautify": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", - "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", "dependencies": { "config-chain": "^1.1.13", "editorconfig": "^1.0.4", - "glob": "^10.3.3", + "glob": "^10.4.2", "js-cookie": "^3.0.5", - "nopt": "^7.2.0" + "nopt": "^7.2.1" }, "bin": { "css-beautify": "js/bin/css-beautify.js", @@ -13196,22 +11378,20 @@ } }, "node_modules/js-beautify/node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -13241,21 +11421,22 @@ } }, "node_modules/jsdom": { - "version": "25.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", - "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", + "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", + "dev": true, "dependencies": { - "cssstyle": "^4.1.0", + "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.4.3", - "form-data": "^4.0.0", + "form-data": "^4.0.1", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", + "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.12", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.7.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.0.0", @@ -13263,7 +11444,7 @@ "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0", + "whatwg-url": "^14.1.0", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, @@ -13271,7 +11452,7 @@ "node": ">=18" }, "peerDependencies": { - "canvas": "^2.11.2" + "canvas": "^3.0.0" }, "peerDependenciesMeta": { "canvas": { @@ -13280,10 +11461,9 @@ } }, "node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "bin": { "jsesc": "bin/jsesc" }, @@ -13306,12 +11486,12 @@ } }, "node_modules/json-diff": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/json-diff/-/json-diff-0.9.1.tgz", - "integrity": "sha512-Z3eMg6kbVTBr3g/IpmF6z28yL2sqi4kkIlJWDQSt0auQW2y0QFu3RmSKizvj880E/EUyNanvWY3oi/T5Z7ZA1g==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/json-diff/-/json-diff-1.0.6.tgz", + "integrity": "sha512-tcFIPRdlc35YkYdGxcamJjllUhXWv4n2rK9oJ2RsAzV4FBkuV4ojKEDgcZ+kpKxDmJKv+PFK65+1tVVOnSeEqA==", "dependencies": { - "cli-color": "^2.0.0", - "difflib": "~0.2.1", + "@ewoudenberg/difflib": "0.1.0", + "colors": "^1.4.0", "dreamopt": "~0.8.0" }, "bin": { @@ -13321,6 +11501,11 @@ "node": "*" } }, + "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", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -13400,9 +11585,9 @@ } }, "node_modules/jstoxml": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/jstoxml/-/jstoxml-3.2.10.tgz", - "integrity": "sha512-c1v2CFxKrhIPWr+cOrBOJWkClka5RfUWG12Y1gqyFD4dzKTlxkarniklwOT/6wR1Zfl+x2ZVIjRIQR9DFiG1rQ==" + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/jstoxml/-/jstoxml-5.0.2.tgz", + "integrity": "sha512-p/Uyi1nSlAcOL+FbWCbTLAHtMbk/QlPMAE/wRLek7W8646jWII3GtLEKSBzf97UitieRWj1VZcbZxs8arq2nbg==" }, "node_modules/jszip": { "version": "3.10.1", @@ -13446,7 +11631,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", - "license": "MIT", "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -13457,7 +11641,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "license": "MIT", "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" @@ -13526,11 +11709,14 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -13731,14 +11917,6 @@ "yallist": "^3.0.2" } }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -13775,14 +11953,6 @@ "marked": "^12.0.2" } }, - "node_modules/markdown-diff/node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/markdown-diff/node_modules/marked": { "version": "12.0.2", "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", @@ -13800,9 +11970,9 @@ "integrity": "sha512-lYrp7FXmBqpmGmsEF92WnSukdgYvLm15FPIODZOx9+3nobkxJxjBYcszqZf5VqTjBtISPSNC7zjU9o3zwpL6AQ==" }, "node_modules/marked": { - "version": "15.0.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.5.tgz", - "integrity": "sha512-xN+kSuqHjxWg+Q47yhhZMUP+kO1qHobvXkkm6FX+7N6lDvanLDd8H7AQ0jWDDyq+fDt/cSrJaBGyWYHXy0KQWA==", + "version": "15.0.7", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.7.tgz", + "integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -13811,6 +11981,14 @@ "node": ">= 18" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -13819,21 +11997,6 @@ "node": ">= 0.6" } }, - "node_modules/memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -13973,9 +12136,9 @@ } }, "node_modules/mocha": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.0.1.tgz", - "integrity": "sha512-+3GkODfsDG71KSCQhc4IekSW+ItCK/kiez1Z28ksWvYhKXV/syxMlerR/sC7whDp7IyreZ4YxceMLdTs5hQE8A==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", + "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", "dev": true, "dependencies": { "ansi-colors": "^4.1.3", @@ -13995,8 +12158,8 @@ "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { @@ -14007,6 +12170,21 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/mocha/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/mocha/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -14035,26 +12213,24 @@ "node": ">=8" } }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/mocha/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/mocha/node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, + "color-name": "~1.1.4" + }, "engines": { - "node": ">=0.3.1" + "node": ">=7.0.0" } }, + "node_modules/mocha/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/mocha/node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -14090,21 +12266,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/mocha/node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -14133,28 +12294,10 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/mock-fs": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.4.1.tgz", - "integrity": "sha512-sz/Q8K1gXXXHR+qr0GZg2ysxCRr323kuN10O7CtQjraJsFDJ4SJ+0I5MzALz7aRp9lHk8Cc/YdsT95h9Ka1aFw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-5.5.0.tgz", + "integrity": "sha512-d/P1M/RacgM3dB0sJ8rjeRNXxtapkPCUnMGmIN0ixJ16F/E4GUZCvWcSGfWGz8eaXYvn1s9baUwNjI4LOPEjiA==", "dev": true, "engines": { "node": ">=12.0.0" @@ -14206,9 +12349,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz", + "integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==", "funding": [ { "type": "github", @@ -14235,11 +12378,6 @@ "node": ">= 0.6" } }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -14256,14 +12394,14 @@ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" }, "node_modules/nopt": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", - "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "dependencies": { "abbrev": "^2.0.0" }, @@ -14275,13 +12413,12 @@ } }, "node_modules/normalize-package-data": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.1.tgz", - "integrity": "sha512-6rvCfeRW+OEZagAB4lMLSNuTNYZWLVtKccK79VSTf//yTY5VOCgcpH80O+bZK8Neps7pUnd5G+QlMg1yV/2iZQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", "dev": true, "dependencies": { "hosted-git-info": "^7.0.0", - "is-core-module": "^2.8.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, @@ -14383,6 +12520,13 @@ "which", "write-file-atomic" ], + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^8.0.0", @@ -14476,14 +12620,28 @@ } }, "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dependencies": { - "path-key": "^3.0.0" + "path-key": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm/node_modules/@isaacs/cliui": { @@ -16872,9 +15030,10 @@ "license": "ISC" }, "node_modules/nwsapi": { - "version": "2.2.13", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", - "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==" + "version": "2.2.18", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.18.tgz", + "integrity": "sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA==", + "dev": true }, "node_modules/object-assign": { "version": "4.1.1", @@ -16893,9 +15052,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "engines": { "node": ">= 0.4" }, @@ -16903,6 +15062,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-treeify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-4.0.1.tgz", + "integrity": "sha512-Y6tg5rHfsefSkfKujv2SwHulInROy/rCL5F4w0QOWxut8AnxYxf0YmNhTh95Zfyxpsudo66uqkux0ACFnyMSgQ==", + "engines": { + "node": ">= 16" + } + }, "node_modules/objects-to-csv": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/objects-to-csv/-/objects-to-csv-1.3.6.tgz", @@ -16911,42 +15078,146 @@ "async-csv": "^2.1.3" } }, - "node_modules/oclif": { - "version": "4.17.7", - "resolved": "https://registry.npmjs.org/oclif/-/oclif-4.17.7.tgz", - "integrity": "sha512-RWh3TieJHjs2FqUQUHowEbDhriLvdzI3I7LpMbO/sNaA33MvI0B7gg8bO6ZNmBz1f9aN8hBW6wl9s6PHSO4HAQ==", + "node_modules/oclif": { + "version": "4.17.34", + "resolved": "https://registry.npmjs.org/oclif/-/oclif-4.17.34.tgz", + "integrity": "sha512-zog6l7Xndexoq0lQIKyHIspr0OQQBiXQ97xTCZC4hUmgxKoxLVUV4HmHfegAxiTC/5Kmp5+z72b+BysNU2RlVQ==", + "dev": true, + "dependencies": { + "@aws-sdk/client-cloudfront": "^3.758.0", + "@aws-sdk/client-s3": "^3.749.0", + "@inquirer/confirm": "^3.1.22", + "@inquirer/input": "^2.2.4", + "@inquirer/select": "^2.5.0", + "@oclif/core": "^4.2.8", + "@oclif/plugin-help": "^6.2.25", + "@oclif/plugin-not-found": "^3.2.44", + "@oclif/plugin-warn-if-update-available": "^3.1.31", + "async-retry": "^1.3.3", + "chalk": "^4", + "change-case": "^4", + "debug": "^4.4.0", + "ejs": "^3.1.10", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^8.1", + "github-slugger": "^2", + "got": "^13", + "lodash": "^4.17.21", + "normalize-package-data": "^6", + "semver": "^7.7.1", + "sort-package-json": "^2.14.0", + "tiny-jsonc": "^1.0.1", + "validate-npm-package-name": "^5.0.1" + }, + "bin": { + "oclif": "bin/run.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/oclif/node_modules/@inquirer/confirm": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz", + "integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/oclif/node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "dev": true, + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/oclif/node_modules/@inquirer/core/node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "dev": true, + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/oclif/node_modules/@inquirer/input": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz", + "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/oclif/node_modules/@inquirer/select": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", + "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/oclif/node_modules/@inquirer/type": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", + "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", + "dev": true, + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/oclif/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "@aws-sdk/client-cloudfront": "^3.716.0", - "@aws-sdk/client-s3": "^3.717.0", - "@inquirer/confirm": "^3.1.22", - "@inquirer/input": "^2.2.4", - "@inquirer/select": "^2.5.0", - "@oclif/core": "^4.2.0", - "@oclif/plugin-help": "^6.2.20", - "@oclif/plugin-not-found": "^3.2.32", - "@oclif/plugin-warn-if-update-available": "^3.1.28", - "async-retry": "^1.3.3", - "chalk": "^4", - "change-case": "^4", - "debug": "^4.4.0", - "ejs": "^3.1.10", - "find-yarn-workspace-root": "^2.0.0", - "fs-extra": "^8.1", - "github-slugger": "^2", - "got": "^13", - "lodash": "^4.17.21", - "normalize-package-data": "^6", - "semver": "^7.6.3", - "sort-package-json": "^2.10.1", - "tiny-jsonc": "^1.0.1", - "validate-npm-package-name": "^5.0.1" - }, - "bin": { - "oclif": "bin/run.js" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/oclif/node_modules/chalk": { @@ -16965,6 +15236,24 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/oclif/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/oclif/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/oclif/node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -16988,6 +15277,15 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/oclif/node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/oclif/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -17009,6 +15307,20 @@ "node": ">= 4.0.0" } }, + "node_modules/oclif/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -17082,17 +15394,17 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -17161,8 +15473,7 @@ "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, "node_modules/pako": { "version": "1.0.11", @@ -17170,9 +15481,9 @@ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, "node_modules/papaparse": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", - "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==" + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.2.tgz", + "integrity": "sha512-PZXg8UuAc4PcVwLosEEDYjPyfWnTEhOrUfdv+3Bx+NuAb+5NhDmXzg5fHWmdCh1mP5p7JAZfFr3IMQfcntNAdA==" }, "node_modules/param-case": { "version": "3.0.4", @@ -17197,20 +15508,15 @@ } }, "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dependencies": { - "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, "node_modules/parse-passwd": { @@ -17225,6 +15531,7 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dev": true, "dependencies": { "entities": "^4.5.0" }, @@ -17232,6 +15539,18 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -17305,18 +15624,14 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "node_modules/path-type": { "version": "4.0.0", @@ -17340,14 +15655,14 @@ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, "node_modules/perfect-scrollbar": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz", - "integrity": "sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g==" + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.6.tgz", + "integrity": "sha512-rixgxw3SxyJbCaSpo1n35A/fwI1r2rdwMKOTCg/AcG+xOEyZcE8UHVjpZMFCVImzsFoCZeJTT+M/rdEIQYO2nw==" }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -17445,9 +15760,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -17463,25 +15778,25 @@ } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "engines": { - "node": ">=14.0.0" + "node": ">=10.0.0" }, "peerDependencies": { "postcss": "^8.0.0" @@ -17506,25 +15821,19 @@ } }, "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" }, "engines": { - "node": ">= 14" + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" }, "peerDependencies": { "postcss": ">=8.0.9", @@ -17540,22 +15849,27 @@ } }, "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" + "node": ">=10" + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" } }, "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", + "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", "dependencies": { - "postcss-selector-parser": "^6.0.11" + "postcss-selector-parser": "^6.0.10" }, "engines": { "node": ">=12.0" @@ -17569,9 +15883,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -17620,17 +15934,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/proc-log": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", @@ -17727,9 +16030,9 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -17749,6 +16052,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, "engines": { "node": ">=6" } @@ -17850,9 +16154,9 @@ } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/read-cache": { "version": "1.0.0", @@ -17973,6 +16277,24 @@ "validate-npm-package-license": "^3.0.1" } }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/read-pkg/node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -18018,8 +16340,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp-tree": { "version": "0.1.27", @@ -18043,10 +16364,9 @@ } }, "node_modules/registry-auth-token": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.3.tgz", - "integrity": "sha512-1bpc9IyC+e+CNFRaWyn77tk4xGG4PPUyfakSmA6F6cvUDjrm58dfyJ3II+9yb10EDkHoy1LaPSmHaWLOH3m6HA==", - "license": "MIT", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", + "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", "dependencies": { "@pnpm/npm-conf": "^2.1.0" }, @@ -18092,17 +16412,20 @@ } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -18154,9 +16477,9 @@ } }, "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "engines": { "node": ">=10" } @@ -18186,9 +16509,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -18206,6 +16529,8 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -18217,9 +16542,10 @@ } }, "node_modules/rrweb-cssom": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", - "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==" + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true }, "node_modules/run-applescript": { "version": "7.0.0", @@ -18272,9 +16598,9 @@ } }, "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dependencies": { "tslib": "^2.1.0" } @@ -18321,14 +16647,15 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, "dependencies": { "xmlchars": "^2.2.0" }, @@ -18337,9 +16664,9 @@ } }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "bin": { "semver": "bin/semver.js" }, @@ -18383,6 +16710,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/sentence-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", @@ -18417,30 +16752,6 @@ "node": ">= 0.8.0" } }, - "node_modules/serve-static/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -18462,23 +16773,74 @@ "node": ">=8" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -18488,9 +16850,15 @@ } }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/simple-swizzle": { "version": "0.2.2", @@ -18534,6 +16902,36 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -18551,19 +16949,19 @@ "dev": true }, "node_modules/sort-package-json": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.10.1.tgz", - "integrity": "sha512-d76wfhgUuGypKqY72Unm5LFnMpACbdxXsLPcL27pOsSrmVqH3PztFp1uq+Z22suk15h7vXmTesuh2aEjdCqb5w==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.15.1.tgz", + "integrity": "sha512-9x9+o8krTT2saA9liI4BljNjwAbvUnWf11Wq+i/iZt8nl2UGYnf3TH5uBydE7VALmP7AGwlfszuEeL8BDyb0YA==", "dev": true, "dependencies": { "detect-indent": "^7.0.1", "detect-newline": "^4.0.0", "get-stdin": "^9.0.0", "git-hooks-list": "^3.0.0", - "globby": "^13.1.2", "is-plain-obj": "^4.1.0", "semver": "^7.6.0", - "sort-object-keys": "^1.1.3" + "sort-object-keys": "^1.1.3", + "tinyglobby": "^0.2.9" }, "bin": { "sort-package-json": "cli.js" @@ -18581,49 +16979,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sort-package-json/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dev": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sort-package-json/node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sort-package-json/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -18633,9 +16988,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -18676,9 +17031,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", "dev": true }, "node_modules/sprintf-js": { @@ -18725,7 +17080,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", - "license": "MIT", "engines": { "node": ">=4", "npm": ">=6" @@ -18877,9 +17231,15 @@ } }, "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.0.5.tgz", + "integrity": "sha512-YAT3K/sgpCUxhxNMrrdhtod3jckkpYwH6JAuwmUdXZsmzH1wUyzTMrrK2wYCEEqlKwrWDd35NeuUkbBy/1iK+Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ] }, "node_modules/sucrase": { "version": "3.35.0", @@ -18911,22 +17271,20 @@ } }, "node_modules/sucrase/node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -18945,29 +17303,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -18982,13 +17317,13 @@ "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true }, "node_modules/table": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", - "license": "BSD-3-Clause", "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", @@ -19001,55 +17336,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.13.tgz", + "integrity": "sha512-gbvFrB0fOsTv/OugXWi2PtflJ4S6/ctu6Mmn3bCftmLY/6xRsQVEJPgIIpABwpZ52DpONkCA3bEj5b54MHxF2Q==" }, "node_modules/test-exclude": { "version": "6.0.0", @@ -19123,36 +17412,71 @@ "readable-stream": "2 || 3" } }, - "node_modules/timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "node_modules/tiny-jsonc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tiny-jsonc/-/tiny-jsonc-1.0.2.tgz", + "integrity": "sha512-f5QDAfLq6zIVSyCZQZhhyl0QS6MvAyTxgz4X4x3+EoCktNWEYJ6PeoEA97fyb98njpBNNi88ybpD7m+BDFXaCw==", + "dev": true + }, + "node_modules/tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "dev": true, "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tiny-jsonc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tiny-jsonc/-/tiny-jsonc-1.0.1.tgz", - "integrity": "sha512-ik6BCxzva9DoiEfDX/li0L2cWKPPENYvixUprFdl3YPi4bZZUhDnNI9YUkacrv+uIG90dnxR5mNqaoD6UhD6Bw==", - "dev": true + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/tldts": { - "version": "6.1.61", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.61.tgz", - "integrity": "sha512-rv8LUyez4Ygkopqn+M6OLItAOT9FF3REpPQDkdMx5ix8w4qkuE7Vo2o/vw1nxKQYmJDV8JpAMJQr1b+lTKf0FA==", + "version": "6.1.84", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.84.tgz", + "integrity": "sha512-aRGIbCIF3teodtUFAYSdQONVmDRy21REM3o6JnqWn5ZkQBJJ4gHxhw6OfwQ+WkSAi3ASamrS4N4nyazWx6uTYg==", + "dev": true, "dependencies": { - "tldts-core": "^6.1.61" + "tldts-core": "^6.1.84" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.61", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.61.tgz", - "integrity": "sha512-In7VffkDWUPgwa+c9picLUxvb0RltVwTkSgMNFgvlGSWveCzGBemBqTsgJCL4EDFWZ6WH0fKTsot6yNhzy3ZzQ==" + "version": "6.1.84", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.84.tgz", + "integrity": "sha512-NaQa1W76W2aCGjXybvnMYzGSM4x8fvG2AN/pla7qxcg0ZHbooOPhA8kctmOZUDfZyhDL27OGNbwAeig8P4p1vg==", + "dev": true }, "node_modules/tmp": { "version": "0.2.3", @@ -19168,14 +17492,6 @@ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -19196,9 +17512,10 @@ } }, "node_modules/tough-cookie": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", - "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, "dependencies": { "tldts": "^6.1.32" }, @@ -19210,6 +17527,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, "dependencies": { "punycode": "^2.3.1" }, @@ -19226,9 +17544,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "engines": { "node": ">=16" @@ -19243,9 +17561,9 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, "node_modules/ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "version": "29.2.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.6.tgz", + "integrity": "sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg==", "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", @@ -19254,7 +17572,7 @@ "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.6.3", + "semver": "^7.7.1", "yargs-parser": "^21.1.1" }, "bin": { @@ -19289,94 +17607,26 @@ } } }, - "node_modules/ts-jest/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/ts-mocha": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-10.0.0.tgz", - "integrity": "sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==", - "dev": true, - "dependencies": { - "ts-node": "7.0.1" - }, - "bin": { - "ts-mocha": "bin/ts-mocha" - }, - "engines": { - "node": ">= 6.X.X" - }, - "optionalDependencies": { - "tsconfig-paths": "^3.5.0" - }, - "peerDependencies": { - "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X" - } - }, - "node_modules/ts-mocha/node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ts-mocha/node_modules/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/ts-mocha/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ts-mocha/node_modules/ts-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", - "dev": true, - "dependencies": { - "arrify": "^1.0.0", - "buffer-from": "^1.1.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.6", - "yn": "^2.0.0" - }, - "bin": { - "ts-node": "dist/bin.js" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ts-mocha/node_modules/yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", + "node_modules/ts-mocha": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-11.1.0.tgz", + "integrity": "sha512-yT7FfzNRCu8ZKkYvAOiH01xNma/vLq6Vit7yINKYFNVP8e5UyrYXSOMIipERTpzVKJQ4Qcos5bQo1tNERNZevQ==", "dev": true, + "bin": { + "ts-mocha": "bin/ts-mocha" + }, "engines": { - "node": ">=4" + "node": ">= 6.X.X" + }, + "peerDependencies": { + "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X || ^11.X.X", + "ts-node": "^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X", + "tsconfig-paths": "^4.X.X" + }, + "peerDependenciesMeta": { + "tsconfig-paths": { + "optional": true + } } }, "node_modules/ts-node": { @@ -19421,45 +17671,12 @@ } } }, - "node_modules/ts-node/node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "optional": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "optional": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "optional": true, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "engines": { - "node": ">=4" + "node": ">=0.3.1" } }, "node_modules/tsimportlib": { @@ -19493,6 +17710,17 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/tw-elements": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/tw-elements/-/tw-elements-1.1.0.tgz", @@ -19507,66 +17735,22 @@ "tailwindcss": "3.3.0" } }, - "node_modules/tw-elements/node_modules/postcss-import": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", - "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } + "node_modules/tw-elements/node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, - "node_modules/tw-elements/node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } + "node_modules/tw-elements/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/tw-elements/node_modules/postcss-nested": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", - "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, + "node_modules/tw-elements/node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" + "node": ">=10" } }, "node_modules/tw-elements/node_modules/tailwindcss": { @@ -19610,19 +17794,6 @@ "postcss": "^8.0.9" } }, - "node_modules/tw-elements/node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -19636,9 +17807,9 @@ } }, "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "engines": { "node": ">=4" } @@ -19667,9 +17838,9 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -19681,8 +17852,7 @@ "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" }, "node_modules/universalify": { "version": "2.0.1", @@ -19701,9 +17871,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", @@ -19720,7 +17890,7 @@ ], "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -19770,9 +17940,9 @@ } }, "node_modules/uuid": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", - "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -19794,9 +17964,9 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" }, "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -19844,6 +18014,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, "dependencies": { "xml-name-validator": "^5.0.0" }, @@ -19863,6 +18034,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, "engines": { "node": ">=12" } @@ -19871,6 +18043,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, "dependencies": { "iconv-lite": "0.6.3" }, @@ -19882,6 +18055,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -19893,14 +18067,16 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, "engines": { "node": ">=18" } }, "node_modules/whatwg-url": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", - "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.1.tgz", + "integrity": "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==", + "dev": true, "dependencies": { "tr46": "^5.0.0", "webidl-conversions": "^7.0.0" @@ -19910,17 +18086,17 @@ } }, "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "node-which": "bin/node-which" + "node-which": "bin/which.js" }, "engines": { - "node": ">= 8" + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/widest-line": { @@ -19968,6 +18144,15 @@ "node": ">= 12.0.0" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -20012,6 +18197,66 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -20029,10 +18274,16 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "dev": true, "engines": { "node": ">=10.0.0" }, @@ -20060,36 +18311,32 @@ "sax": "^1.2.4" } }, - "node_modules/xlsx-populate/node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" - }, "node_modules/xml-formatter": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-3.6.2.tgz", - "integrity": "sha512-enWhevZNOwffZFUhzl1WMcha8lFLZUgJ7NzFs5Ug4ZOFCoNheGYXz1J9Iz/e+cTn9rCkuT1GwTacz+YlmFHOGw==", + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-3.6.4.tgz", + "integrity": "sha512-vkvTNw4u9mp72lMmJHw771NE9EJLX0kfwIcP+ZEo9eJ6HmotX23vmykyROyIQ9Y3a+ckdUdhxIE2ZO66rYuPrg==", "dependencies": { - "xml-parser-xo": "^4.1.0" + "xml-parser-xo": "^4.1.2" }, "engines": { - "node": ">= 14" + "node": ">= 16" } }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, "engines": { "node": ">=18" } }, "node_modules/xml-parser-xo": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-4.1.1.tgz", - "integrity": "sha512-Ggf2y90+Y6e9IK5hoPuembVHJ03PhDSdhldEmgzbihzu9k0XBo0sfcFxaSi4W1PlUSSI1ok+MJ0JCXUn+U4Ilw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-4.1.3.tgz", + "integrity": "sha512-U6eN5Pyrlek9ottHVpT9e8YUax75oVYXbnYxU+utzDC7i+OyWj9ynsNMiZNQZvpuazbG0O7iLAs9FkcFmzlgSA==", "engines": { - "node": ">= 14" + "node": ">= 16" } }, "node_modules/xml2js": { @@ -20115,7 +18362,8 @@ "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true }, "node_modules/xmlhttprequest": { "version": "1.8.0", @@ -20167,12 +18415,11 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -20211,12 +18458,26 @@ "flat": "cli.js" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, "engines": { - "node": ">=12" + "node": ">=8" + } + }, + "node_modules/yarn": { + "version": "1.22.22", + "resolved": "https://registry.npmjs.org/yarn/-/yarn-1.22.22.tgz", + "integrity": "sha512-prL3kGtyG7o9Z9Sv8IPfBNrWTDmXB4Qbes8A9rEzt6wkJV8mUvoirjU0Mp3GGAU06Y0XQyA3/2/RQFVuK7MTfg==", + "hasInstallScript": true, + "bin": { + "yarn": "bin/yarn.js", + "yarnpkg": "bin/yarn.js" + }, + "engines": { + "node": ">=4.0.0" } }, "node_modules/yauzl": { @@ -20232,9 +18493,9 @@ } }, "node_modules/yazl": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/yazl/-/yazl-3.2.1.tgz", - "integrity": "sha512-srBrMa97OdczYkvARd8nVaJrDu8vRVqzuaCfP/Fz8yQMjuUiIJchQUz5ouaoN31pp2Crk3UFblz621Q/mtp+6g==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-3.3.1.tgz", + "integrity": "sha512-BbETDVWG+VcMUle37k5Fqp//7SDOK2/1+T7X8TD96M3D9G8jK5VLUdQVdVjGi8im7FGkazX7kk5hkU8X4L5Bng==", "dependencies": { "buffer-crc32": "^1.0.0" } diff --git a/package.json b/package.json index 88bab7eb1..d8a3da7b1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@mitre/saf", "description": "The MITRE Security Automation Framework (SAF) Command Line Interface (CLI) brings together applications, techniques, libraries, and tools developed by MITRE and the security community to streamline security automation for systems and DevOps pipelines.", - "version": "1.4.17", + "version": "1.4.23", "author": "The MITRE Security Automation Framework", "bin": "./bin/run", "bugs": "https://github.com/mitre/saf/issues", @@ -11,17 +11,17 @@ "@azure/identity": "^4.3.0", "@microsoft/microsoft-graph-client": "^3.0.7", "@microsoft/microsoft-graph-types": "^2.40.0", - "@mitre/emass_client": "3.10.0", - "@mitre/hdf-converters": "2.11.1", - "@mitre/heimdall-lite": "2.11.1", - "@mitre/inspec-objects": "1.0.1", + "@mitre/emass_client": "3.22.0", + "@mitre/hdf-converters": "2.11.5", + "@mitre/heimdall-lite": "2.11.5", + "@mitre/inspec-objects": "2.0.1", "@oclif/core": "^4.0.15", "@oclif/plugin-help": "^6.0.9", "@oclif/plugin-plugins": "^5.0.14", "@oclif/plugin-version": "^2.0.11", "@oclif/plugin-warn-if-update-available": "^3.0.15", - "@smithy/node-http-handler": "^3.0.0", - "@types/chai": "^4", + "@smithy/node-http-handler": "^4.0.0", + "@types/chai": "4.3.3", "@types/express": "^5.0.0", "@types/fs-extra": "^11.0.1", "@types/get-installed-path": "^4.0.1", @@ -39,13 +39,13 @@ "ajv": "^8.12.0", "app-root-path": "^3.1.0", "axios": "^1.5.0", - "chai": "^4", + "chai": "4.5.0", "chalk": "^5.4.1", "colors": "^1.4.0", "csv-parse": "^4.16.0", "dotenv": "^16.3.1", "express": "^4.17.3", - "fast-xml-parser": "^4.2.7", + "fast-xml-parser": "^5.0.7", "flat": "^6.0.1", "form-data": "^4.0.0", "fs-extra": "^11.1.1", @@ -53,13 +53,12 @@ "get-installed-path": "^4.0.8", "htmlparser2": "^10.0.0", "https": "^1.0.0", - "inquirer": "12.3.0", + "inquirer": "12.4.3", "inquirer-file-selector": "^0.6.1", - "inspecjs": "^2.10.16", + "inspecjs": "2.11.0", "jest": "^29.7.0", "jest-mock": "^29.7.0", "js-yaml": "^4.1.0", - "jsdom": "^25.0.1", "json-colorizer": "^3.0.1", "lodash": "^4.17.21", "markdown-diff": "^2.0.0", @@ -75,7 +74,7 @@ "ts-node": "^10", "tsimportlib": "^0.0.5", "tslib": "^2", - "typescript": "~5.7", + "typescript": "~5.8", "uuid": "^11.0.2", "winston": "^3.10.0", "xlsx-populate": "^1.21.0", @@ -94,12 +93,13 @@ "eslint-config-oclif": "^4.0", "eslint-config-oclif-typescript": "^1.0.3", "eslint-plugin-unicorn": "^56.0.0", + "jsdom": "^26.0.0", "marked": "^15.0.0", "mocha": "^11", "mock-fs": "^5.2.0", "oclif": "^4.8.8", "tmp": "^0.2.1", - "ts-mocha": "^10.0.0" + "ts-mocha": "^11.1.0" }, "engines": { "node": "^22.0.0" @@ -118,14 +118,21 @@ "oclif": { "commands": "./lib/commands", "helpClass": "./lib/utils/oclif/help/help", + "hooks": { + "command_not_found": "./lib/utils/oclif/hooks/command_not_found" + }, "theme": "./oclif-theme.json", "additionalHelpFlags": [ "-h", + "-H", "--tell-me-more", "--explain" ], "additionalVersionFlags": [ - "-v" + "-v", + "-V", + "--version", + "--Version" ], "bin": "saf", "plugins": [ diff --git a/release-prep.ps1 b/release-prep.ps1 index b5d6c2406..44e05b27e 100644 --- a/release-prep.ps1 +++ b/release-prep.ps1 @@ -37,8 +37,8 @@ Write-Output "# - Commit previously staged files with 'signoff' tag (new vers Write-Output "# - Tag the commit with new release version " | Yellow Write-Output "# - Push and updated the repository three references (new version number) " | Yellow Write-Output "# Prerequisites: " | Yellow -Write-Output "# - Before executing the preparatory script ensure that the you're on a " | Yellow -Write-Output "# directory containing the most recent commit of the SAF CLI. " | Yellow +Write-Output "# - Before executing the preparatory script ensure that the you're on a " | Red +Write-Output "# directory containing the most recent commit of the SAF CLI. " | Red Write-Output "# - Windows PowerShell 6. PowerShell version less than 6.0.0 will malformed " | Red Write-Output "# the output, pretty print does not work properly. " | Red Write-Output "# (see PowerShell Prettier formatting for ConvertTo-Json output PR #2736) " | Red @@ -62,7 +62,6 @@ if ($currentVersion -ge $requiredVersion) { TerminateScript } - #------------------------------------------------------------------------------ # Start the Script Write-Output "Press enter to continue - or type exit/EXIT to terminate" | Green @@ -120,7 +119,7 @@ Write-Output "$CYAN Setting SAF CLI version to: $nextVersion" | Green # 4. Update the package.json and VERSION files $jsonObject.version = $nextVersion $jsonObject | ConvertTo-Json -Depth 3 | Set-Content -Path "package.json" -[System.IO.File]::WriteAllText("VERSION", $nextVersion) +$nextVersion | Out-File -FilePath "VERSION" Write-Output "Done" | Green Write-Host diff --git a/release-prep.sh b/release-prep.sh index 2cf1cffb2..e4da4f4ba 100755 --- a/release-prep.sh +++ b/release-prep.sh @@ -43,8 +43,8 @@ PrintColor "Yellow" "# - Commit previously staged files with 'signoff' tag (n PrintColor "Yellow" "# - Tag the commit with new release version " PrintColor "Yellow" "# - Push and updated the repository three references (new version number) " PrintColor "Yellow" "# Prerequisites: " -PrintColor "Yellow" "# - Before executing the preparatory script ensure that the you're on a " -PrintColor "Yellow" "# directory containing the most recent commit of the SAF CLI. " +PrintColor "Red" "# - Before executing the preparatory script ensure that the you're on a " +PrintColor "Red" "# directory containing the most recent commit of the SAF CLI. " PrintColor "Yellow" "#------------------------------------------------------------------------------" #------------------------------------------------------------------------------ diff --git a/src/commands/emasser/configure.ts b/src/commands/emasser/configure.ts index 66cb1dac2..757098a4e 100644 --- a/src/commands/emasser/configure.ts +++ b/src/commands/emasser/configure.ts @@ -3,29 +3,38 @@ import {Command} from '@oclif/core' import {generateConfig} from '../../utils/emasser/generateConfig' export default class EmasserBuildConfig extends Command { - static summary = 'Generate a configuration file (.env) for accessing an eMASS instances.' + static readonly summary = 'Generate a configuration file (.env) for accessing an eMASS instances.\n' + + 'Authentication to an eMASS instances requires a PKI-valid/trusted client\n' + + 'certificate. The eMASSer CLI accepts a Key/Client pair certificates (.pem) or\n' + + 'a CA certificate (.pem or .crt). A Unique user identifier (user-uid) is used by\n' + + 'most eMASS integration, however certain integrations, the user-uid is not required' - static description = + static readonly description = ` - ${colors.yellow('The following variables are required:')} + ${colors.yellow('Required eMASS configuration variables 👇')} ${colors.blue('\tEMASSER_API_KEY') + colors.green(' 30 alpha numeric characters>\b')} - ${colors.blue('\tEMASSER_USER_UID') + colors.green(' \b')} ${colors.blue('\tEMASSER_HOST_URL') + colors.green(' ')} - ${colors.blue('\tEMASSER_KEY_FILE_PATH') + colors.green(' ')} - ${colors.blue('\tEMASSER_CERT_FILE_PATH') + colors.green(' ')} - ${colors.blue('\tEMASSER_KEY_FILE_PASSWORD') + colors.green(' ')} - ${colors.yellow('The following variables are optional, if not provided defaults are used:')} + ${colors.blue('\tEMASSER_KEY_FILE_PATH') + colors.green(' ')} + ${colors.blue('\tEMASSER_CERT_FILE_PATH') + colors.green(' ')} + ${colors.blue('\tEMASSER_CA_FILE_PATH') + colors.green(' ')} + ${colors.blue('\tEMASSER_KEY_FILE_PASSWORD') + colors.green(' ')} + ${colors.yellow('Certain eMASS integrations may not require (most do) this variable 👇')} + ${colors.blue('\tEMASSER_USER_UID') + colors.green(' \b')} + + ${colors.yellow('Optional eMASS configuration variables, if not provided defaults are used 👇')} ${colors.blue('\tEMASSER_PORT') + colors.green(' \b')} ${colors.blue('\tEMASSER_REQUEST_CERT') + colors.green(' ')} ${colors.blue('\tEMASSER_REJECT_UNAUTHORIZED') + colors.green(' ')} ${colors.blue('\tEMASSER_DEBUGGING') + colors.green(' ')} ${colors.blue('\tEMASSER_CLI_DISPLAY_NULL') + colors.green(' ')} ${colors.blue('\tEMASSER_EPOCH_TO_DATETIME') + colors.green(' ')} + ${colors.blue('\tEMASSER_DOWNLOAD_DIR') + colors.green(' ')} ` - static examples = ['<%= config.bin %> <%= command.id %>'] + static readonly examples = ['<%= config.bin %> <%= command.id %>'] - async run(): Promise { // skipcq: JS-0116, JS-0105 - generateConfig() + // skipcq: JS-0116, JS-0105 + async run(): Promise { + generateConfig() // skipcq: JS-0328 } } diff --git a/src/commands/emasser/delete/artifacts.ts b/src/commands/emasser/delete/artifacts.ts index 1256c2920..58b6ee9df 100644 --- a/src/commands/emasser/delete/artifacts.ts +++ b/src/commands/emasser/delete/artifacts.ts @@ -10,15 +10,16 @@ import {ArtifactsApi} from '@mitre/emass_client' import {ArtifactsResponseDel, ArtifactsRequestDeleteBodyInner as ArtifactDeleteBody} from '@mitre/emass_client/dist/api' +const CMD_HELP = 'saf emasser delete artifacts -h or --help' export default class EmasserDeleteArtifacts extends Command { - static usage = '<%= command.id %> [options]'; + static readonly usage = '<%= command.id %> [FLAGS]'; - static description = 'Remove one or many artifacts in a system identified by system Id'; + static readonly description = 'Remove one or many artifacts in a system identified by system Id'; - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-F,--fileName]']; + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--fileName] ...'] - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the DELETE POA&M endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show help for the SAF CLI eMASSer DELETE Artifacts command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -36,4 +37,14 @@ export default class EmasserDeleteArtifacts extends Command { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/delete/cloud_resources.ts b/src/commands/emasser/delete/cloud_resources.ts new file mode 100644 index 000000000..821c92eb7 --- /dev/null +++ b/src/commands/emasser/delete/cloud_resources.ts @@ -0,0 +1,53 @@ +import {colorize} from 'json-colorizer' +import {Command, Flags} from '@oclif/core' + +import {outputError} from '../../../utils/emasser/outputError' +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' + +import {CloudResourceResultsApi} from '@mitre/emass_client' +import { + CloudResourcesDeleteBodyInner, + CloudResourcesPostDelete, +} from '@mitre/emass_client/dist/api' + +const CMD_HELP = 'saf emasser delete cloud_resources -h or --help' +export default class EmasserDeleteCloudResources extends Command { + static readonly usage = '<%= command.id %> [FLAGS]'; + + static readonly description = 'Remove one or multiple containers in a system identified by system Id'; + + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-r,--resourceId] ...'] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show help for the SAF CLI eMASSer DELETE Cloud Resources command'}), + ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 + } + + async run(): Promise { + const {flags} = await this.parse(EmasserDeleteCloudResources) + const apiCxn = new ApiConnection() + const cloudResource = new CloudResourceResultsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + const requestBodyArray: Array = [] + flags.resourceId.forEach((resourceId: string) => { + requestBodyArray.push({resourceId: resourceId.replace(',', '')}) + }) + + // Call the API + cloudResource.deleteCloudResources(flags.systemId, requestBodyArray).then((response: CloudResourcesPostDelete) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } +} diff --git a/src/commands/emasser/delete/container_scans.ts b/src/commands/emasser/delete/container_scans.ts new file mode 100644 index 000000000..cb3779b9e --- /dev/null +++ b/src/commands/emasser/delete/container_scans.ts @@ -0,0 +1,53 @@ +import {colorize} from 'json-colorizer' +import {Command, Flags} from '@oclif/core' + +import {outputError} from '../../../utils/emasser/outputError' +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' + +import {ContainerScanResultsApi} from '@mitre/emass_client' +import { + ContainerResourcesDeleteBodyInner, + ContainersResourcesPostDelete, +} from '@mitre/emass_client/dist/api' + +const CMD_HELP = 'saf emasser delete container_scans -h or --help' +export default class EmasserContainerScans extends Command { + static readonly usage = '<%= command.id %> [FLAGS]'; + + static readonly description = 'Remove one or multiple containers in a system identified by system Id'; + + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-c,--containerId] ...'] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show help for the SAF CLI eMASSer DELETE Container Scans command'}), + ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 + } + + async run(): Promise { + const {flags} = await this.parse(EmasserContainerScans) + const apiCxn = new ApiConnection() + const containerScan = new ContainerScanResultsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + const requestBodyArray: Array = [] + flags.containerId.forEach((containerId: string) => { + requestBodyArray.push({containerId: containerId.replace(',', '')}) + }) + + // Call the API + containerScan.deleteContainerSans(flags.systemId, requestBodyArray).then((response: ContainersResourcesPostDelete) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } +} diff --git a/src/commands/emasser/delete/hardware_baseline.ts b/src/commands/emasser/delete/hardware_baseline.ts new file mode 100644 index 000000000..c9cf77275 --- /dev/null +++ b/src/commands/emasser/delete/hardware_baseline.ts @@ -0,0 +1,51 @@ +import {colorize} from 'json-colorizer' +import {Command, Flags} from '@oclif/core' + +import {outputError} from '../../../utils/emasser/outputError' +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' + +import {HardwareBaselineApi} from '@mitre/emass_client' +import {HwBaselineResponseDelete, + HwBaselineRequestDeleteBodyInner as HwDeleteBody} from '@mitre/emass_client/dist/api' + +const CMD_HELP = 'saf emasser delete hardware_baseline -h or --help' +export default class EmasserDeleteHardwareBaseline extends Command { + static readonly usage = '<%= command.id %> [FLAGS]'; + + static readonly description = 'Remove one or many Hardware items in a system identified by system and hardware Id'; + + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-a,--assetsHardwareId] ...']; + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show help for the SAF CLI eMASSer DELETE Hardware Baseline command'}), + ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 + } + + async run(): Promise { + const {flags} = await this.parse(EmasserDeleteHardwareBaseline) + const apiCxn = new ApiConnection() + const delHwBaseline = new HardwareBaselineApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + const requestBodyArray: HwDeleteBody[] = [] + flags.assetsHardwareId.forEach((hardwareId: string) => { + requestBodyArray.push({hardwareId: hardwareId}) // skipcq: JS-0240 + }) + + // Call the endpoint + delHwBaseline.deleteHwBaselineAssets(flags.systemId, requestBodyArray).then((response: HwBaselineResponseDelete) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } +} diff --git a/src/commands/emasser/delete/milestones.ts b/src/commands/emasser/delete/milestones.ts index b8867ed34..b56b37936 100644 --- a/src/commands/emasser/delete/milestones.ts +++ b/src/commands/emasser/delete/milestones.ts @@ -10,15 +10,16 @@ import {MilestonesApi} from '@mitre/emass_client' import {MilestonesPutPostDelete, MilestonesRequestDeleteBodyInner as MilestoneDeleteBody} from '@mitre/emass_client/dist/api' +const CMD_HELP = 'saf emasser delete milestones -h or --help' export default class EmasserDeleteMilestones extends Command { - static usage = '<%= command.id %> [options]'; + static readonly usage = '<%= command.id %> [FLAGS]'; - static description = 'Remove milestones in a system for one or many POA&M items identified by system, poam, and milestone Id'; + static readonly description = 'Remove milestones in a system for one or many POA&M items identified by system, poam, and milestone Id'; - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-p,--poamId] [-M,--milestonesId]']; + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-p,--poamId] [-m,--milestonesId] ...']; - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the DELETE Milestones endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show help for the SAF CLI eMASSer DELETE Milestones command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -32,8 +33,19 @@ export default class EmasserDeleteMilestones extends Command { requestBodyArray.push({milestoneId: milestoneId}) // skipcq: JS-0240 }) + // Call API endpoint delMilestones.deleteMilestone(flags.systemId, flags.poamId, requestBodyArray).then((response: MilestonesPutPostDelete) => { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/delete/poams.ts b/src/commands/emasser/delete/poams.ts index 72fadb55e..431784708 100644 --- a/src/commands/emasser/delete/poams.ts +++ b/src/commands/emasser/delete/poams.ts @@ -7,18 +7,19 @@ import {outputFormat} from '../../../utils/emasser/outputFormatter' import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' import {POAMApi} from '@mitre/emass_client' -import {PoamResponseDelete, +import {PoamResponsePostPutDelete, PoamRequestDeleteBodyInner as PoamDeleteBody} from '@mitre/emass_client/dist/api' +const CMD_HELP = 'saf emasser delete poams -h or --help' export default class EmasserDeletePoams extends Command { - static usage = '<%= command.id %> [options]'; + static readonly usage = '<%= command.id %> [FLAGS]'; - static description = 'Remove one or many POA&M items in a system identified by system and poam Id'; + static readonly description = 'Remove one or many POA&M items in a system identified by system and poam Id'; - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-P,--poamsId]']; + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-p,--poamsId] ...']; - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the DELETE POA&M endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show help for the SAF CLI eMASSer DELETE POA&Ms command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -32,8 +33,19 @@ export default class EmasserDeletePoams extends Command { requestBodyArray.push({poamId: poamId}) // skipcq: JS-0240 }) - delPoam.deletePoam(flags.systemId, requestBodyArray).then((response: PoamResponseDelete) => { + // Call the endpoint + delPoam.deletePoam(flags.systemId, requestBodyArray).then((response: PoamResponsePostPutDelete) => { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/delete/software_baseline.ts b/src/commands/emasser/delete/software_baseline.ts new file mode 100644 index 000000000..72fac0283 --- /dev/null +++ b/src/commands/emasser/delete/software_baseline.ts @@ -0,0 +1,51 @@ +import {colorize} from 'json-colorizer' +import {Command, Flags} from '@oclif/core' + +import {outputError} from '../../../utils/emasser/outputError' +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' + +import {SoftwareBaselineApi} from '@mitre/emass_client' +import {SwBaselineResponseDelete, + SwBaselineRequestDeleteBodyInner as SwDeleteBody} from '@mitre/emass_client/dist/api' + +const CMD_HELP = 'saf emasser delete software_baseline -h or --help' +export default class EmasserDeleteSoftwareBaseline extends Command { + static readonly usage = '<%= command.id %> [FLAGS]'; + + static readonly description = 'Remove one or many Software items in a system identified by system and software Id'; + + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-a,--assetsSoftwareId] ...']; + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show help for the SAF CLI eMASSer DELETE Software Baseline command'}), + ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 + } + + async run(): Promise { + const {flags} = await this.parse(EmasserDeleteSoftwareBaseline) + const apiCxn = new ApiConnection() + const delSwBaseline = new SoftwareBaselineApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + const requestBodyArray: SwDeleteBody[] = [] + flags.assetsSoftwareId.forEach((softwareId: string) => { + requestBodyArray.push({softwareId: softwareId}) // skipcq: JS-0240 + }) + + // Call the endpoint + delSwBaseline.deleteSwBaselineAssets(flags.systemId, requestBodyArray).then((response: SwBaselineResponseDelete) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } +} diff --git a/src/commands/emasser/get/artifacts.ts b/src/commands/emasser/get/artifacts.ts index c85e9671b..9b4b6a150 100644 --- a/src/commands/emasser/get/artifacts.ts +++ b/src/commands/emasser/get/artifacts.ts @@ -1,6 +1,7 @@ import {colorize} from 'json-colorizer' import {Args, Command, Flags} from '@oclif/core' import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {ApiConfig} from '../../../utils/emasser/apiConfig' import {ArtifactsApi, ArtifactsExportApi} from '@mitre/emass_client' import {ArtifactsResponseGet} from '@mitre/emass_client/dist/api' import {outputFormat} from '../../../utils/emasser/outputFormatter' @@ -8,27 +9,28 @@ import {outputError} from '../../../utils/emasser/outputError' import {FlagOptions, getDescriptionForEndpoint, getExamplesForEndpoint, - getFlagsForEndpoint} from '../../../utils/emasser/utilities' + getFlagsForEndpoint, + saveFile} from '../../../utils/emasser/utilities' const endpoint = 'artifacts' export default class EmasserGetArtifacts extends Command { - static usage = '<%= command.id %> [ARGUMENT] \n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; + static readonly usage = '<%= command.id %> [ARGUMENT] [FLAGS]\n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; - static description = getDescriptionForEndpoint(process.argv, endpoint); + static readonly description = getDescriptionForEndpoint(process.argv, endpoint); - static examples = getExamplesForEndpoint(process.argv); + static readonly examples = getExamplesForEndpoint(process.argv); - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET Artifacts endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Artifacts command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 }; // NOTE: The way args are being implemented are mainly for the purposes of help clarity, there is, displays // the available arguments with associate description. // Only args.name is used, there is, it contains the argument listed by the user. - // Example: If the user uses the command (saf emasser get artifacts forSystem), args.name is set to forSystem - static args = { + // Example: If the user uses the command (saf eMASSer get artifacts forSystem), args.name is set to forSystem + static readonly args = { name: Args.string({name: 'name', required: false, hidden: true}), forSystem: Args.string({name: 'forSystem', description: 'Retrieves available milestones for provided system (Id)', required: false}), export: Args.string({name: 'export', description: 'Exports the milestone(s) for provided system (Id) and file name', required: false}), @@ -48,10 +50,22 @@ export default class EmasserGetArtifacts extends Command { const getArtifactsExport = new ArtifactsExportApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here getArtifactsExport.getSystemArtifactsExport(flags.systemId, flags.filename, flags.compress).then((response: any) => { - if (typeof response.data === 'string') { - console.log(response.data) - } else { - console.log(JSON.stringify(response.data, null, 2)) + const fileName = response.config.url.split('=')[1] + // Zip and compress file data is of type of string output to download directory + try { + if (typeof response.data === 'string') { + const conf = new ApiConfig() + console.log(`\x1B[33mOutput file: ${fileName} saved to directory: ${conf.downloadDir}\x1B[0m`) + saveFile(conf.downloadDir, fileName, response.data) + } else if (flags.printToStdOut) { + console.log(colorize(JSON.stringify(response.data, null, 2))) + } else { + const conf = new ApiConfig() + console.log(`\x1B[33mOutput file: ${fileName} saved to directory: ${conf.downloadDir}\x1B[0m`) + saveFile(conf.downloadDir, fileName, JSON.stringify(response.data)) + } + } catch (error: any) { + console.error(`\x1B[31mSave File Error: ${error.message}\x1B[0m`) } }).catch((error:any) => console.error(colorize(outputError(error)))) } else { @@ -61,7 +75,7 @@ export default class EmasserGetArtifacts extends Command { async catch(error: any) { // skipcq: JS-0116 if (error.message) { - this.error(error) + this.warn(error.message) } else { const suggestions = 'get artifacts [-h or --help]\n\tget artifacts forSystem\n\tget artifacts export' this.warn('Invalid arguments\nTry this:\n\t' + suggestions) diff --git a/src/commands/emasser/get/cac.ts b/src/commands/emasser/get/cac.ts index 48d0784b5..2bc45b531 100644 --- a/src/commands/emasser/get/cac.ts +++ b/src/commands/emasser/get/cac.ts @@ -8,14 +8,14 @@ import {outputError} from '../../../utils/emasser/outputError' import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' export default class EmasserGetCac extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]' - static description = 'View one or many Control Approval Chain (CAC) in a system specified system ID' + static readonly description = 'View one or many Control Approval Chain (CAC) in a system specified system ID' - static examples = ['<%= config.bin %> <%= command.id %> --systemId '] + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s, --systemId] '] - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET CAC endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET CAC command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -29,4 +29,13 @@ export default class EmasserGetCac extends Command { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + async catch(error: any) { // skipcq: JS-0116 + if (error.message) { + this.warn(error.message) + } else { + const suggestions = 'get cac [-h or --help]' + this.warn('Invalid arguments\nTry this:\n\t' + suggestions) + } + } } diff --git a/src/commands/emasser/get/cmmc.ts b/src/commands/emasser/get/cmmc.ts index e680136b1..1540cbfa3 100644 --- a/src/commands/emasser/get/cmmc.ts +++ b/src/commands/emasser/get/cmmc.ts @@ -8,14 +8,14 @@ import {outputError} from '../../../utils/emasser/outputError' import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' export default class EmasserGetCmmc extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAG]' - static description = 'View Cybersecurity Maturity Model Certification (CMMC) Assessments' + static readonly description = 'View Cybersecurity Maturity Model Certification (CMMC) Assessments' - static examples = ['<%= config.bin %> <%= command.id %> --sinceDate '] + static readonly examples = ['<%= config.bin %> <%= command.id %> [-d, --sinceDate] '] - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET CMMC endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET CMMC command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -29,4 +29,13 @@ export default class EmasserGetCmmc extends Command { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + async catch(error: any) { // skipcq: JS-0116 + if (error.message) { + this.warn(error.message) + } else { + const suggestions = 'get cmmc [-h or --help]' + this.warn('Invalid arguments\nTry this:\n\t' + suggestions) + } + } } diff --git a/src/commands/emasser/get/controls.ts b/src/commands/emasser/get/controls.ts index f7d917836..724759427 100644 --- a/src/commands/emasser/get/controls.ts +++ b/src/commands/emasser/get/controls.ts @@ -8,14 +8,14 @@ import {outputError} from '../../../utils/emasser/outputError' import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' export default class EmasserGetControls extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]' - static description = 'Get system Security Control information for both the Implementation Plan and Risk Assessment' + static readonly description = 'Get system Security Control information for both the Implementation Plan and Risk Assessment' - static examples = ['<%= config.bin %> <%= command.id %> --systemId [option]'] + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s, --systemId] [option]'] - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET Controls endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Controls command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -29,4 +29,13 @@ export default class EmasserGetControls extends Command { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + async catch(error: any) { // skipcq: JS-0116 + if (error.message) { + this.warn(error.message) + } else { + const suggestions = 'get controls [-h or --help]' + this.warn('Invalid arguments\nTry this:\n\t' + suggestions) + } + } } diff --git a/src/commands/emasser/get/dashboards.ts b/src/commands/emasser/get/dashboards.ts index cbd39c8a4..2dd4bc537 100644 --- a/src/commands/emasser/get/dashboards.ts +++ b/src/commands/emasser/get/dashboards.ts @@ -1,25 +1,55 @@ import {colorize} from 'json-colorizer' import {Args, Command, Flags} from '@oclif/core' import {ApiConnection} from '../../../utils/emasser/apiConnection' -import {DashboardsApi} from '@mitre/emass_client' import {outputFormat} from '../../../utils/emasser/outputFormatter' import {outputError} from '../../../utils/emasser/outputError' import {FlagOptions, getDescriptionForEndpoint, getExamplesForEndpoint, getFlagsForEndpoint} from '../../../utils/emasser/utilities' +import { + SystemStatusDashboardApi, + SystemTermsConditionsDashboardsApi, + SystemConnectivityCCSDDashboardsApi, + SystemATCIATCDashboardApi, + SystemQuestionnaireDashboardsApi, + SystemWorkflowsDashboardsApi, + SystemSecurityControlsDashboardsApi, + SystemPOAMDashboardsApi, + SystemArtifactsDashboardsApi, + SystemHardwareDashboardsApi, + SystemSensorHardwareDashboardsApi, + SystemSoftwareDashboardsApi, + SystemSensorSoftwareDashboardsApi, + SystemCriticalAssetsDashboardApi, + SystemVulnerabilityDashboardApi, + SystemDeviceFindingsDashboardsApi, + SystemApplicationFindingsDashboardsApi, + SystemPortsProtocolsDashboardsApi, + UserSystemAssignmentsDashboardApi, + SystemAssociationsDashboardApi, + SystemCONMONIntegrationStatusDashboardApi, + OrganizationMigrationStatusDashboardApi, + SystemMigrationStatusDashboardApi, + SystemFISMAMetricsDashboardApi, + CoastGuardSystemFISMAMetricsDashboardApi, + SystemPrivacyDashboardApi, + VAOMBFISMADashboardApi, + VASystemDashboardsApi, + CMMCAssessmentDashboardsApi, +} from '@mitre/emass_client' const endpoint = 'dashboards' export default class EmasserGetDashboards extends Command { - static usage = '<%= command.id %> [ARGUMENT] \n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; + static readonly usage = '<%= command.id %> [ARGUMENT] [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m' - static description = getDescriptionForEndpoint(process.argv, endpoint); + static readonly description = getDescriptionForEndpoint(process.argv, endpoint); - static examples = getExamplesForEndpoint(process.argv, endpoint); + static readonly examples = getExamplesForEndpoint(process.argv, endpoint); - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET Dashboards endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Dashboards command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 }; @@ -27,29 +57,58 @@ export default class EmasserGetDashboards extends Command { // dashboard is being called, and provides the appropriate description. // Only args.name is used, there is, it contains the argument listed by the user, the dashboard name. // Example: If the command is invoked (saf emasser get dashboards status_details), args.name is set to status_details - static args = { + static readonly args = { name: Args.string({name: 'name', required: false, hidden: true}), // System Status Dashboard - status_details: Args.string({name: 'status_details', description: 'Get systems status detail dashboard information', required: false}), - // Enterprise Security Controls Dashboard + status_details: Args.string({name: 'status_details', description: 'Get systems status detail dashboard information', required: false, defaultHelp: 'System Status Details'}), + // System Terms and Conditions Dashboard + terms_conditions_summary: Args.string({name: 'terms_conditions_summary', description: 'Get system terms and conditions summary dashboard information', required: false, defaultHelp: 'System Terms and Conditions Dashboard'}), + terms_conditions_details: Args.string({name: 'terms_conditions_details', description: 'Get system terms and conditions details dashboard information', required: false, defaultHelp: 'System Terms and Conditions Dashboard'}), + // System Connectivity CCSD Dashboard + connectivity_ccsd_summary: Args.string({name: 'connectivity_ccsd_summary', description: 'Get system connectivity CCSD summary dashboard information', required: false}), + connectivity_ccsd_details: Args.string({name: 'connectivity_ccsd_details', description: 'Get system connectivity CCSD details dashboard information', required: false}), + // System ATC/IATC Dashboard + atc_iatc_details: Args.string({name: 'atc_iatc_details', description: 'Get system ATC/IATC details dashboard information', required: false}), + // System Questionnaire Dashboard + questionnaire_summary: Args.string({name: 'questionnaire_summary', description: 'Get system questionnaire summary dashboard information', required: false}), + questionnaire_details: Args.string({name: 'questionnaire_details', description: 'Get system questionnaire details dashboard information', required: false}), + // System Workflows Dashboard + workflows_history_summary: Args.string({name: 'workflow_history_summary', description: 'Get system workflow history summary dashboard information', required: false}), + workflows_history_details: Args.string({name: 'workflow_history_details', description: 'Get system workflow history details dashboard information', required: false}), + workflows_history_stage_details: Args.string({name: 'workflow_history_stage_details', description: 'Get system workflow history stage details dashboard information', required: false}), + // System Security Controls Dashboard control_compliance_summary: Args.string({name: 'control_compliance_summary', description: 'Get control compliance summary dashboard information', required: false}), security_control_details: Args.string({name: 'security_control_details', description: 'Get security control details dashboard information', required: false}), assessment_procedures_details: Args.string({name: 'assessment_procedures_details', description: 'Get assessment procedures details dashboard information', required: false}), - // Enterprise POA&M Dashboard + // System POA&M Dashboard poam_summary: Args.string({name: 'poam_summary', description: 'Get systems POA&Ms summary dashboard information', required: false}), poam_details: Args.string({name: 'poam_details', description: 'Get system POA&Ms details dashboard information', required: false}), - // Enterprise Artifacts Dashboard + // System Artifacts Dashboard artifacts_summary: Args.string({name: 'artifacts_summary', description: 'Get artifacts summary dashboard information', required: false}), artifacts_details: Args.string({name: 'artifacts_details', description: 'Get artifacts details dashboard information', required: false}), - // Hardware Baseline Dashboard + // System Hardware Dashboard hardware_summary: Args.string({name: 'hardware_summary', description: 'Get hardware summary dashboard information', required: false}), hardware_details: Args.string({name: 'hardware_details', description: 'Get hardware details dashboard information', required: false}), - // Enterprise Sensor-based Hardware Resources Dashboard + // System Sensor-based Hardware Resources Dashboard sensor_hardware_summary: Args.string({name: 'sensor_hardware_summary', description: 'Get sensor hardware summary dashboard information', required: false}), sensor_hardware_details: Args.string({name: 'sensor_hardware_details', description: 'Get sensor hardware details dashboard information', required: false}), // Software Baseline Dashboard software_summary: Args.string({name: 'software_summary', description: 'Get software baseline summary dashboard information', required: false}), software_details: Args.string({name: 'software_details', description: 'Get software baseline details dashboard information', required: false}), + // Sensor-based Software Resources Dashboard + sensor_software_summary: Args.string({name: 'sensor_software_summary', description: 'Get sensor software summary dashboard information', required: false}), + sensor_software_details: Args.string({name: 'sensor_software_details', description: 'Get sensor software details dashboard information', required: false}), + sensor_software_counts: Args.string({name: 'sensor_software_counts', description: 'Get sensor software counts dashboard information', required: false}), + // Critical Assets Dashboard + critical_assets_summary: Args.string({name: 'critical_assets_summary', description: 'Get critical assets summary dashboard information', required: false}), + // Vulnerability Dashboard + vulnerability_summary: Args.string({name: 'vulnerability_summary', description: 'Get vulnerability summary dashboard information', required: false}), + // Device Findings Dashboard + device_findings_summary: Args.string({name: 'device_findings_summary', description: 'Get device findings summary dashboard information', required: false}), + device_findings_details: Args.string({name: 'device_findings_details', description: 'Get device findings details dashboard information', required: false}), + // Application Findings Dashboard + application_findings_summary: Args.string({name: 'application_findings_summary', description: 'Get application findings summary dashboard information', required: false}), + application_findings_details: Args.string({name: 'application_findings_details', description: 'Get application findings details dashboard information', required: false}), // Ports and Protocols Dashboard ports_protocols_summary: Args.string({name: 'ports_protocols_summary', description: 'Get ports and protocols summary dashboard information', required: false}), ports_protocols_details: Args.string({name: 'ports_protocols_details', description: 'Get ports and protocols details dashboard information', required: false}), @@ -57,35 +116,147 @@ export default class EmasserGetDashboards extends Command { integration_status_summary: Args.string({name: 'integration_status_summary', description: 'Get CONMON integration status summary dashboard information', required: false}), // System Associations Dashboard associations_details: Args.string({name: 'associations_details', description: 'Get system associations details dashboard information', required: false}), - // Users Dashboard - assignments_details: Args.string({name: 'assignments_details', description: 'Get user system assignments details dashboard information', required: false}), + // User System Assignments Dashboard + user_assignments_details: Args.string({name: 'assignments_details', description: 'Get user system assignments details dashboard information', required: false}), + // Organization Migration Status Dashboard + org_migration_status: Args.string({name: 'org_migration_status', description: 'Get organization migration status dashboard information', required: false}), + // System Migration Status Dashboard + system_migration_status: Args.string({name: 'system_migration_status', description: 'Get system migration status dashboard information', required: false}), + // FISMA Metrics Dashboard + fisma_metrics: Args.string({name: 'fisma_metrics', description: 'Get FISMA metrics dashboard information', required: false}), + // Coast Guard FISMA Metrics Dashboard + coast_guard_fisma_metrics: Args.string({name: 'coast_guard_fisma_metrics', description: 'Get Coast Guard FISMA metrics dashboard information', required: false}), // Privacy Compliance Dashboard privacy_summary: Args.string({name: 'privacy_summary', description: 'Get system privacy summary dashboard information', required: false}), + // VA OMB-FISMA SAOP Summary Dashboard fisma_saop_summary: Args.string({name: 'fisma_saop_summary', description: 'Get VA OMB-FISMA SAOP summary dashboard information', required: false}), - // System A&A Summary Dashboard + // VA Systems Dashboards + va_icamp_tableau_poam_details: Args.string({name: 'va_icamp_tableau_poam_details', description: 'Get VA system ICAMP Tableau POA&M details dashboard information', required: false}), va_aa_summary: Args.string({name: 'va_aa_summary', description: 'Get VA system A&A summary dashboard information', required: false}), - // System A2.0 Summary Dashboard va_a2_summary: Args.string({name: 'va_a2_summary', description: 'Get VA system A2.0 summary dashboard information', required: false}), - // System P.L. 109 Reporting Summary Dashboard va_pl_109_summary: Args.string({name: 'va_pl_109_summary', description: 'Get VA System P.L. 109 reporting summary dashboard information', required: false}), - // FISMA Inventory Summary Dashboard - fisma_inventory_summary: Args.string({name: 'fisma_inventory_summary', description: 'Get VA system FISMA inventory summary dashboard information', required: false}), - fisma_inventory_crypto_summary: Args.string({name: 'fisma_inventory_crypto_summary', description: 'Get VA system FISMA inventory summary dashboard information', required: false}), - // Threat Risks Dashboard + va_fisma_inventory_summary: Args.string({name: 'fisma_inventory_summary', description: 'Get VA system FISMA inventory summary dashboard information', required: false}), + va_fisma_inventory_crypto_summary: Args.string({name: 'fisma_inventory_crypto_summary', description: 'Get VA system FISMA inventory summary dashboard information', required: false}), va_threat_risk_summary: Args.string({name: 'va_threat_risk_summary', description: 'Get VA threat risk summary dashboard information', required: false}), va_threat_source_details: Args.string({name: 'va_threat_source_details', description: 'Get VA threat source details dashboard information', required: false}), va_threat_architecture_details: Args.string({name: 'va_threat_architecture_details', description: 'Get VA threat architecture details dashboard information', required: false}), + // CMMC Assessment Dashboard + cmmc_status_summary: Args.string({name: 'cmmc_status_summary', description: 'Get CMMC assessment status summary dashboard information', required: false}), + cmmc_compliance_summary: Args.string({name: 'cmmc_compliance_summary', description: 'Get CMMC assessment requirements compliance summary dashboard information', required: false}), + cmmc_security_requirements_details: Args.string({name: 'cmmc_security_requirements_details', description: 'Get CMMC assessment security requirements details dashboard information', required: false}), + cmmc_requirement_objectives_details: Args.string({name: 'cmmc_requirement_objectives_details', description: 'Get CMMC assessment requirement objectives details dashboard information', required: false}), }; + // skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold async run(): Promise { // skipcq: JS-0044 const {args, flags} = await this.parse(EmasserGetDashboards) const apiCxn = new ApiConnection() - const getDashboards = new DashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) switch (args.name) { case 'status_details': { + const getDashboard = new SystemStatusDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemStatusDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemStatusDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'terms_conditions_details': { + const getDashboard = new SystemTermsConditionsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemTermsConditionsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'terms_conditions_summary': { + const getDashboard = new SystemTermsConditionsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemTermsConditionsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'connectivity_ccsd_details': { + const getDashboard = new SystemConnectivityCCSDDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemConnectivityCcsdDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'connectivity_ccsd_summary': { + const getDashboard = new SystemConnectivityCCSDDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemConnectivityCcsdSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'atc_iatc_details': { + const getDashboard = new SystemATCIATCDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemAtcIatcDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'questionnaire_summary': { + const getDashboard = new SystemQuestionnaireDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemQuestionnaireSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'questionnaire_details': { + const getDashboard = new SystemQuestionnaireDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemQuestionnaireDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'workflows_history_summary': { + const getDashboard = new SystemWorkflowsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemWorkflowsHistorySummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'workflows_history_details': { + const getDashboard = new SystemWorkflowsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemWorkflowsHistoryDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'workflows_history_stage_details': { + const getDashboard = new SystemWorkflowsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemWorkflowsHistoryStageDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -93,8 +264,9 @@ export default class EmasserGetDashboards extends Command { } case 'control_compliance_summary': { + const getDashboard = new SystemSecurityControlsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemControlComplianceSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemControlComplianceSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -102,8 +274,9 @@ export default class EmasserGetDashboards extends Command { } case 'security_control_details': { + const getDashboard = new SystemSecurityControlsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemSecurityControlDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemSecurityControlDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -111,8 +284,9 @@ export default class EmasserGetDashboards extends Command { } case 'assessment_procedures_details': { + const getDashboard = new SystemSecurityControlsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemAssessmentProceduresDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemAssessmentProceduresDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -120,8 +294,9 @@ export default class EmasserGetDashboards extends Command { } case 'poam_summary': { + const getDashboard = new SystemPOAMDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemPoamSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemPoamSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -129,8 +304,9 @@ export default class EmasserGetDashboards extends Command { } case 'poam_details': { + const getDashboard = new SystemPOAMDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemPoamDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemPoamDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -138,8 +314,9 @@ export default class EmasserGetDashboards extends Command { } case 'artifacts_summary': { + const getDashboard = new SystemArtifactsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemArtifactsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemArtifactsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -147,8 +324,9 @@ export default class EmasserGetDashboards extends Command { } case 'artifacts_details': { + const getDashboard = new SystemArtifactsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemArtifactsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemArtifactsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -156,8 +334,9 @@ export default class EmasserGetDashboards extends Command { } case 'hardware_summary': { + const getDashboard = new SystemHardwareDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemHardwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemHardwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -165,8 +344,9 @@ export default class EmasserGetDashboards extends Command { } case 'hardware_details': { + const getDashboard = new SystemHardwareDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemHardwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemHardwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -174,8 +354,9 @@ export default class EmasserGetDashboards extends Command { } case 'sensor_hardware_summary': { + const getDashboard = new SystemSensorHardwareDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemSensorHardwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemSensorHardwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -183,8 +364,9 @@ export default class EmasserGetDashboards extends Command { } case 'sensor_hardware_details': { + const getDashboard = new SystemSensorHardwareDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemSensorHardwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemSensorHardwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -192,8 +374,9 @@ export default class EmasserGetDashboards extends Command { } case 'software_summary': { + const getDashboard = new SystemSoftwareDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemSoftwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemSoftwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -201,8 +384,99 @@ export default class EmasserGetDashboards extends Command { } case 'software_details': { + const getDashboard = new SystemSoftwareDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemSoftwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'sensor_software_summary': { + const getDashboard = new SystemSensorSoftwareDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemSensorSoftwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'sensor_software_details': { + const getDashboard = new SystemSensorSoftwareDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemSensorSoftwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'sensor_software_counts': { + const getDashboard = new SystemSensorSoftwareDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemSensorSoftwareCounts(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'critical_assets_summary': { + const getDashboard = new SystemCriticalAssetsDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemSoftwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemCriticalAssetsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'vulnerability_summary': { + const getDashboard = new SystemVulnerabilityDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemVulnerabilitySummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'device_findings_summary': { + const getDashboard = new SystemDeviceFindingsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemDeviceFindingsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'device_findings_details': { + const getDashboard = new SystemDeviceFindingsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemDeviceFindingsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'application_findings_summary': { + const getDashboard = new SystemApplicationFindingsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemApplicationFindingsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'application_findings_details': { + const getDashboard = new SystemApplicationFindingsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemApplicationFindingsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -210,8 +484,9 @@ export default class EmasserGetDashboards extends Command { } case 'ports_protocols_summary': { + const getDashboard = new SystemPortsProtocolsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemPortsProtocolsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemPortsProtocolsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -219,8 +494,9 @@ export default class EmasserGetDashboards extends Command { } case 'ports_protocols_details': { + const getDashboard = new SystemPortsProtocolsDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemPortsProtocolsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemPortsProtocolsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -228,8 +504,9 @@ export default class EmasserGetDashboards extends Command { } case 'integration_status_summary': { + const getDashboard = new SystemCONMONIntegrationStatusDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemCommonIntegrationStatusSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemCommonIntegrationStatusSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -237,17 +514,59 @@ export default class EmasserGetDashboards extends Command { } case 'associations_details': { + const getDashboard = new SystemAssociationsDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemAssociationsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'user_assignments_details': { + const getDashboard = new UserSystemAssignmentsDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getUserSystemAssignmentsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'org_migration_status': { + const getDashboard = new OrganizationMigrationStatusDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemAssociationsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getOrganizationMigrationStatusSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) break } - case 'assignments_details': { + case 'system_migration_status': { + const getDashboard = new SystemMigrationStatusDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getUserSystemAssignmentsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemMigrationStatusSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'fisma_metrics': { + const getDashboard = new SystemFISMAMetricsDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getSystemFismaMetrics(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'coast_guard_fisma_metrics': { + const getDashboard = new CoastGuardSystemFISMAMetricsDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getCoastGuardSystemFismaMetrics(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -255,8 +574,9 @@ export default class EmasserGetDashboards extends Command { } case 'privacy_summary': { + const getDashboard = new SystemPrivacyDashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getSystemPrivacySummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getSystemPrivacySummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -264,8 +584,19 @@ export default class EmasserGetDashboards extends Command { } case 'fisma_saop_summary': { + const getDashboard = new VAOMBFISMADashboardApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getVaOmbFsmaSaopSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'va_icamp_tableau_poam_details': { + const getDashboard = new VASystemDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getVaOmbFsmaSaopSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getVaSystemIcampTableauPoamDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -273,8 +604,9 @@ export default class EmasserGetDashboards extends Command { } case 'va_aa_summary': { + const getDashboard = new VASystemDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getVaSystemAaSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getVaSystemAaSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -282,8 +614,9 @@ export default class EmasserGetDashboards extends Command { } case 'va_a2_summary': { + const getDashboard = new VASystemDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getVaSystemA2Summary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getVaSystemA2Summary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -291,26 +624,29 @@ export default class EmasserGetDashboards extends Command { } case 'va_pl_109_summary': { + const getDashboard = new VASystemDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getVaSystemPl109ReportingSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getVaSystemPl109ReportingSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) break } - case 'fisma_inventory_summary': { + case 'va_fisma_inventory_summary': { + const getDashboard = new VASystemDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getVaSystemFismaInvetorySummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getVaSystemFismaInvetorySummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) break } - case 'fisma_inventory_crypto_summary': { + case 'va_fisma_inventory_crypto_summary': { + const getDashboard = new VASystemDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getVaSystemFismaInvetoryCryptoSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getVaSystemFismaInvetoryCryptoSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -318,8 +654,9 @@ export default class EmasserGetDashboards extends Command { } case 'va_threat_risk_summary': { + const getDashboard = new VASystemDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getVaSystemThreatRiskSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getVaSystemThreatRiskSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -327,8 +664,9 @@ export default class EmasserGetDashboards extends Command { } case 'va_threat_source_details': { + const getDashboard = new VASystemDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getVaSystemThreatSourceDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getVaSystemThreatSourceDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -336,8 +674,49 @@ export default class EmasserGetDashboards extends Command { } case 'va_threat_architecture_details': { + const getDashboard = new VASystemDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getVaSystemThreatArchitectureDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'cmmc_status_summary': { + const getDashboard = new CMMCAssessmentDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getCmmcAssessmentStatusSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'cmmc_compliance_summary': { + const getDashboard = new CMMCAssessmentDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getCmmcAssessmentRequirementsComplianceSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'cmmc_security_requirements_details': { + const getDashboard = new CMMCAssessmentDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + // Order is important here + getDashboard.getCmmcAssessmentSecurityRequirementsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + + break + } + + case 'cmmc_requirement_objectives_details': { + const getDashboard = new CMMCAssessmentDashboardsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getDashboards.getVaSystemThreatArchitectureDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { + getDashboard.getCmmcAssessmentRequirementObjectivesDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) @@ -352,7 +731,7 @@ export default class EmasserGetDashboards extends Command { async catch(error: any) { // skipcq: JS-0116 if (error.message) { - this.warn(error) + this.warn(error.message) } else { const suggestions = 'get dashboards [-h or --help] for available arguments' this.warn('Invalid arguments\nTry this:\n\t' + suggestions) diff --git a/src/commands/emasser/get/hardware.ts b/src/commands/emasser/get/hardware.ts new file mode 100644 index 000000000..c5a4b830a --- /dev/null +++ b/src/commands/emasser/get/hardware.ts @@ -0,0 +1,64 @@ +import {colorize} from 'json-colorizer' +import {Args, Command, Flags} from '@oclif/core' +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {HardwareBaselineApi} from '@mitre/emass_client' +import {HwBaselineResponseGet} from '@mitre/emass_client/dist/api' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import {outputError} from '../../../utils/emasser/outputError' +import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' + +export default class EmasserGetHardwareBaseline extends Command { + static readonly usage = '<%= command.id %> [ARGUMENT] [FLAGS]\n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; + + static readonly description = 'View all hardware baseline for a system available on the eMASS instance' + + static readonly examples = [ + { + description: '\x1B[93mRetrieve baselines without pagination\x1B[0m', + command: '<%= config.bin %> <%= command.id %> baseline [-s, --systemId] [options]', + }, + { + description: '\x1B[93mRetrieve baselines with pagination\x1B[0m', + command: '<%= config.bin %> <%= command.id %> baseline [-s, --systemId] [-S, --pageSize]= [-i, --pageIndex]=', + }, + ] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Hardware Baseline command'}), + ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 + } + + // NOTE: The way args are being implemented are mainly for clarity purposes, there is, it displays + // the available arguments with associate description. + // Only args.name is used, there is, it contains the argument listed by the user. + // Example: If the user uses the command (saf emasser get hardware baseline), args.name is set to baseline + static readonly args = { + name: Args.string({name: 'name', required: false, hidden: true}), + baseline: Args.string({name: 'baseline', description: 'Retrieves all hardware baseline for a system', required: false}), + } + + async run(): Promise { + const {args, flags} = await this.parse(EmasserGetHardwareBaseline) + const apiCxn = new ApiConnection() + const getHardwareBaseline = new HardwareBaselineApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + if (args.name === 'baseline') { + // Order is important here + getHardwareBaseline.getSystemHwBaseline(flags.systemId, flags.pageIndex, flags.pageSize) + .then((response: HwBaselineResponseGet) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + } else { + throw this.error + } + } + + async catch(error: any) { // skipcq: JS-0116 + if (error.message) { + this.warn(error) + } else { + const suggestions = 'get hardware [-h or --help]\n\tget hardware baseline' + this.warn('Invalid arguments\nTry this 👇:\n\t' + suggestions) + } + } +} diff --git a/src/commands/emasser/get/milestones.ts b/src/commands/emasser/get/milestones.ts index 356d98d1e..e09c34853 100644 --- a/src/commands/emasser/get/milestones.ts +++ b/src/commands/emasser/get/milestones.ts @@ -13,14 +13,14 @@ import {FlagOptions, const endpoint = 'milestones' export default class EmasserGetMilestones extends Command { - static usage = '<%= command.id %> [ARGUMENT] \n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; + static readonly usage = '<%= command.id %> [ARGUMENT] [FLAGS] \n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; - static description = getDescriptionForEndpoint(process.argv, endpoint); + static readonly description = getDescriptionForEndpoint(process.argv, endpoint); - static examples = getExamplesForEndpoint(process.argv, endpoint); + static readonly examples = getExamplesForEndpoint(process.argv, endpoint); - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET Milestones endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Milestones command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -28,7 +28,7 @@ export default class EmasserGetMilestones extends Command { // the available arguments with associate description. // Only args.name is used, there is, it contains the argument listed by the user. // Example: If the user uses the command (saf emasser get milestones byPoamId), args.name is set to byPoamId - static args = { + static readonly args = { name: Args.string({name: 'name', required: false, hidden: true}), byPoamId: Args.string({name: 'byPoamId', description: 'Retrieves milestone(s) for specified system and poam Id', required: false}), byMilestoneId: Args.string({name: 'byMilestoneId', description: 'Retrieves milestone(s) for specified system, poam, and milestone Id', required: false}), @@ -56,7 +56,7 @@ export default class EmasserGetMilestones extends Command { async catch(error: any) { // skipcq: JS-0116 if (error.message) { - this.error(error) + this.warn(error.message) } else { const suggestions = 'get milestones [-h or --help]\n\tget milestones byPoamId\n\tget milestones byMilestoneId' this.warn('Invalid arguments\nTry this:\n\t' + suggestions) diff --git a/src/commands/emasser/get/pac.ts b/src/commands/emasser/get/pac.ts index 9ddedd798..e794608a8 100644 --- a/src/commands/emasser/get/pac.ts +++ b/src/commands/emasser/get/pac.ts @@ -8,14 +8,14 @@ import {outputError} from '../../../utils/emasser/outputError' import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' export default class EmasserGetPac extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAG]' - static description = 'View one or many Package Approval Chain (PAC) in a system specified system ID' + static readonly description = 'View one or many Package Approval Chain (PAC) in a system specified system ID' - static examples = ['<%= config.bin %> <%= command.id %> --systemId '] + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s, --systemId] '] - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET PAC endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET PAC command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -29,4 +29,13 @@ export default class EmasserGetPac extends Command { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + async catch(error: any) { // skipcq: JS-0116 + if (error.message) { + this.warn(error.message) + } else { + const suggestions = 'get pac [-h or --help]' + this.warn('Invalid arguments\nTry this:\n\t' + suggestions) + } + } } diff --git a/src/commands/emasser/get/poams.ts b/src/commands/emasser/get/poams.ts index 93b1afe61..821aa6a7e 100644 --- a/src/commands/emasser/get/poams.ts +++ b/src/commands/emasser/get/poams.ts @@ -13,14 +13,14 @@ import {FlagOptions, const endpoint = 'poams' export default class EmasserGetPoams extends Command { - static usage = '<%= command.id %> [ARGUMENT] \n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; + static readonly usage = '<%= command.id %> [ARGUMENT] [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; - static description = getDescriptionForEndpoint(process.argv, endpoint); + static readonly description = getDescriptionForEndpoint(process.argv, endpoint); - static examples = getExamplesForEndpoint(process.argv, endpoint); + static readonly examples = getExamplesForEndpoint(process.argv, endpoint); - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET POA&Ms endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET POA&Ms command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -28,7 +28,7 @@ export default class EmasserGetPoams extends Command { // the available arguments with associate description. // Only args.name is used, there is, it contains the argument listed by the user. // Example: If the user uses the command (saf emasser get poams byPoamId), args.name is set to byPoamId - static args = { + static readonly args = { name: Args.string({name: 'name', required: false, hidden: true}), forSystem: Args.string({name: 'forSystem', description: 'Retrieves Poams for specified system ID', required: false}), byPoamId: Args.string({name: 'byPoamId', description: 'Retrieves Poams for specified system and poam ID', required: false}), @@ -56,7 +56,7 @@ export default class EmasserGetPoams extends Command { async catch(error: any) { // skipcq: JS-0116 if (error.message) { - this.error(error) + this.warn(error.message) } else { const suggestions = 'get poams [-h or --help]\n\tget poams forSystem\n\tget poams byPoamId' this.warn('Invalid arguments\nTry this:\n\t' + suggestions) diff --git a/src/commands/emasser/get/roles.ts b/src/commands/emasser/get/roles.ts index 38fc5280a..2ba0f2e18 100644 --- a/src/commands/emasser/get/roles.ts +++ b/src/commands/emasser/get/roles.ts @@ -13,14 +13,14 @@ import {FlagOptions, const endpoint = 'roles' export default class EmasserGetRoles extends Command { - static usage = '<%= command.id %> [ARGUMENT] \n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; + static readonly usage = '<%= command.id %> [ARGUMENT] [FLAGS]\n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; - static description = getDescriptionForEndpoint(process.argv, endpoint); + static readonly description = getDescriptionForEndpoint(process.argv, endpoint); - static examples = getExamplesForEndpoint(process.argv, endpoint); + static readonly examples = getExamplesForEndpoint(process.argv, endpoint); - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET Roles endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Roles command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -28,7 +28,7 @@ export default class EmasserGetRoles extends Command { // the available arguments with associate description. // Only args.name is used, there is, it contains the argument listed by the user. // Example: If the user uses the command (saf emasser get roles byCategory), args.name is set to byCategory - static args = { + static readonly args = { name: Args.string({name: 'name', required: false, hidden: true}), all: Args.string({name: 'all', description: 'Retrieves all available system roles', required: false}), byCategory: Args.string({name: 'byCategory', description: 'Retrieves role(s) - filtered by [options] params', required: false}), @@ -56,10 +56,10 @@ export default class EmasserGetRoles extends Command { async catch(error: any) { // skipcq: JS-0116 if (error.message) { - this.error(error) + this.warn(error.message) } else { const suggestions = 'get roles [-h or --help]\n\tget roles all\n\tget roles byCategory' - this.warn('Invalid arguments\nTry this:\n\t' + suggestions) + this.warn('Invalid arguments\nTry this 👇:\n\t' + suggestions) } } } diff --git a/src/commands/emasser/get/software.ts b/src/commands/emasser/get/software.ts new file mode 100644 index 000000000..46005c570 --- /dev/null +++ b/src/commands/emasser/get/software.ts @@ -0,0 +1,64 @@ +import {colorize} from 'json-colorizer' +import {Args, Command, Flags} from '@oclif/core' +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {SoftwareBaselineApi} from '@mitre/emass_client' +import {SwBaselineResponseGet} from '@mitre/emass_client/dist/api' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import {outputError} from '../../../utils/emasser/outputError' +import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' + +export default class EmasserGetSoftwareBaseline extends Command { + static readonly usage = '<%= command.id %> [ARGUMENT] [FLAGS]\n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; + + static readonly description = 'View all software baseline for a system available on the eMASS instance' + + static readonly examples = [ + { + description: '\x1B[93mRetrieve baselines without pagination\x1B[0m', + command: '<%= config.bin %> <%= command.id %> baseline [-s, --systemId] [options]', + }, + { + description: '\x1B[93mRetrieve baselines with pagination\x1B[0m', + command: '<%= config.bin %> <%= command.id %> baseline [-s, --systemId] [-S, --pageSize]= [-i, --pageIndex]=', + }, + ] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Software Baseline command'}), + ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 + } + + // NOTE: The way args are being implemented are mainly for clarity purposes, there is, it displays + // the available arguments with associate description. + // Only args.name is used, there is, it contains the argument listed by the user. + // Example: If the user uses the command (saf emasser get software baseline), args.name is set to baseline + static args = { + name: Args.string({name: 'name', required: false, hidden: true}), + baseline: Args.string({name: 'baseline', description: 'Retrieves all software baseline for a system', required: false}), + } + + async run(): Promise { + const {args, flags} = await this.parse(EmasserGetSoftwareBaseline) + const apiCxn = new ApiConnection() + const getHardwareBaseline = new SoftwareBaselineApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + if (args.name === 'baseline') { + // Order is important here + getHardwareBaseline.getSystemSwBaseline(flags.systemId, flags.pageIndex, flags.pageSize) + .then((response: SwBaselineResponseGet) => { + console.log(colorize(outputFormat(response))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + } else { + throw this.error + } + } + + async catch(error: any) { // skipcq: JS-0116 + if (error.message) { + this.warn(error) + } else { + const suggestions = 'get software [-h or --help]\n\tget software baseline' + this.warn('Invalid arguments\nTry this 👇:\n\t' + suggestions) + } + } +} diff --git a/src/commands/emasser/get/system.ts b/src/commands/emasser/get/system.ts index c098ac9bc..7f97dfeb4 100644 --- a/src/commands/emasser/get/system.ts +++ b/src/commands/emasser/get/system.ts @@ -8,14 +8,14 @@ import {outputError} from '../../../utils/emasser/outputError' import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' export default class EmasserGetSystem extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAG]' - static description = 'Get system information for a specific system defined by ID (systemId)' + static readonly description = 'Get system information for a specific system defined by ID (systemId)' - static examples = ['<%= config.bin %> <%= command.id %> [-s, --systemId] [options]'] + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s, --systemId] [options]'] - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET System endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET System command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -29,5 +29,14 @@ export default class EmasserGetSystem extends Command { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + async catch(error: any) { // skipcq: JS-0116 + if (error.message) { + this.warn(error.message) + } else { + const suggestions = 'get system [-h or --help]' + this.warn('Invalid arguments\nTry this 👇:\n\t' + suggestions) + } + } } diff --git a/src/commands/emasser/get/systems.ts b/src/commands/emasser/get/systems.ts index 943dce59c..fbf5f37dc 100644 --- a/src/commands/emasser/get/systems.ts +++ b/src/commands/emasser/get/systems.ts @@ -1,21 +1,21 @@ import {colorize} from 'json-colorizer' import {Command, Flags} from '@oclif/core' import {ApiConnection} from '../../../utils/emasser/apiConnection' -import {SystemsApi} from '@mitre/emass_client' import {outputFormat} from '../../../utils/emasser/outputFormatter' import {outputError} from '../../../utils/emasser/outputError' import {getFlagsForEndpoint, FlagOptions} from '../../../utils/emasser/utilities' +import {SystemsApi} from '@mitre/emass_client' import {SystemsResponse} from '@mitre/emass_client/dist/api' export default class EmasserGetSystems extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]' - static description = 'Get available systems filter on provided options' + static readonly description = 'Get available systems filter on provided options' - static examples = ['<%= config.bin %> <%= command.id %> [options]'] + static readonly examples = ['<%= config.bin %> <%= command.id %> [options]'] - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET Systems endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Systems command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -25,8 +25,21 @@ export default class EmasserGetSystems extends Command { const getSystems = new SystemsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) // Order is important here - getSystems.getSystems(flags.includePackage, flags.registrationType, flags.ditprId, flags.coamsId, flags.policy, flags.includeDitprMetrics, flags.includeDecommissioned, flags.reportsForScorecard).then((response: SystemsResponse) => { + getSystems.getSystems( + // eslint-disable-next-line function-call-argument-newline + flags.includePackage, flags.registrationType, flags.ditprId, flags.coamsId, + flags.policy, flags.includeDitprMetrics, flags.includeDecommissioned, flags.reportsForScorecard, + ).then((response: SystemsResponse) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + async catch(error: any) { // skipcq: JS-0116 + if (error.message) { + this.warn(error.message) + } else { + const suggestions = 'get systems [-h or --help]' + this.warn('Invalid arguments\nTry this 👇:\n\t' + suggestions) + } + } } diff --git a/src/commands/emasser/get/test_connection.ts b/src/commands/emasser/get/test_connection.ts index ad7eb2980..cb58ce571 100644 --- a/src/commands/emasser/get/test_connection.ts +++ b/src/commands/emasser/get/test_connection.ts @@ -7,14 +7,14 @@ import {outputFormat} from '../../../utils/emasser/outputFormatter' import {outputError} from '../../../utils/emasser/outputError' export default class EmasserGetTestConnection extends Command { - static usage = '<%= command.id %>' + static readonly usage = '<%= command.id %>' - static description = 'Test if eMASS url is set to a correct host' + static readonly description = 'Test if eMASSer is properly configured to a valid eMASS URL\nUse the eMASSer CLI command "saf emasser configure" to generate or update an eMASS configuration file.' - static examples = ['<%= config.bin %> <%= command.id %>'] + static readonly examples = ['<%= config.bin %> <%= command.id %>'] - static flags = { - help: Flags.help({char: 'h', description: 'Test connection to configured eMASS URL'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Test Connection command'}), } async run(): Promise { // skipcq: JS-0105, JS-0116 @@ -25,4 +25,13 @@ export default class EmasserGetTestConnection extends Command { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + async catch(error: any) { // skipcq: JS-0116 + if (error.message) { + this.warn(error.message) + } else { + const suggestions = 'get test_connection [-h or --help]' + this.warn('Invalid arguments\nTry this 👇:\n\t' + suggestions) + } + } } diff --git a/src/commands/emasser/get/test_results.ts b/src/commands/emasser/get/test_results.ts index 519294774..ccd17839a 100644 --- a/src/commands/emasser/get/test_results.ts +++ b/src/commands/emasser/get/test_results.ts @@ -8,14 +8,14 @@ import {outputError} from '../../../utils/emasser/outputError' import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' export default class EmasserGetTestResults extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]' - static description = 'Get test results for a specific system defined by ID (systemId)' + static readonly description = 'Get test results for a specific system defined by ID (systemId)' - static examples = ['<%= config.bin %> <%= command.id %> --systemId [options]'] + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s, --systemId] [options]'] - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET Test Results endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Test Results command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -29,4 +29,13 @@ export default class EmasserGetTestResults extends Command { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + async catch(error: any) { // skipcq: JS-0116 + if (error.message) { + this.warn(error.message) + } else { + const suggestions = 'get test_results [-h or --help]' + this.warn('Invalid arguments\nTry this 👇:\n\t' + suggestions) + } + } } diff --git a/src/commands/emasser/get/workflow_definitions.ts b/src/commands/emasser/get/workflow_definitions.ts index 7f2f275dc..4c645fbed 100644 --- a/src/commands/emasser/get/workflow_definitions.ts +++ b/src/commands/emasser/get/workflow_definitions.ts @@ -8,14 +8,14 @@ import {outputError} from '../../../utils/emasser/outputError' import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' export default class EmasserGetWorkflowDefinitions extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]' - static description = 'View all workflow schemas available on the eMASS instance' + static readonly description = 'View all workflow schemas available on the eMASS instance' - static examples = ['<%= config.bin %> <%= command.id %> [options]'] + static readonly examples = ['<%= config.bin %> <%= command.id %> [options]'] - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET Workflow Definitions endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Workflow Definitions command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } diff --git a/src/commands/emasser/get/workflow_instances.ts b/src/commands/emasser/get/workflow_instances.ts index debea7189..fc14e89dc 100644 --- a/src/commands/emasser/get/workflow_instances.ts +++ b/src/commands/emasser/get/workflow_instances.ts @@ -14,14 +14,14 @@ import {FlagOptions, const endpoint = 'workflow_instances' export default class EmasserGetWorkflowInstances extends Command { - static usage = '<%= command.id %> [ARGUMENT] \n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m'; + static readonly usage = '<%= command.id %> [ARGUMENT] [FLAGS]\n \x1B[93m NOTE: see EXAMPLES for argument case format\x1B[0m' - static description = getDescriptionForEndpoint(process.argv, endpoint); + static readonly description = getDescriptionForEndpoint(process.argv, endpoint) - static examples = getExamplesForEndpoint(process.argv); + static readonly examples = getExamplesForEndpoint(process.argv) - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the GET Workflow Instances endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the GET Workflow Instances command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -57,10 +57,10 @@ export default class EmasserGetWorkflowInstances extends Command { async catch(error: any) { // skipcq: JS-0116 if (error.message) { - this.error(error) + this.warn(error) } else { const suggestions = 'get workflow_instances [-h or --help]\n\tget workflow_instances all\n\tget workflow_instances byInstanceId' - this.warn('Invalid arguments\nTry this:\n\t' + suggestions) + this.warn('Invalid arguments\nTry this 👇:\n\t' + suggestions) } } } diff --git a/src/commands/emasser/hello.ts b/src/commands/emasser/hello.ts index f4a10effa..eb6036d98 100644 --- a/src/commands/emasser/hello.ts +++ b/src/commands/emasser/hello.ts @@ -3,14 +3,15 @@ import {name, version} from '@mitre/emass_client/package.json' import os from 'os' export default class EmasserSayHello extends Command { - static hidden = true + static readonly hidden = true async run(): Promise { // skipcq: JS-0116, JS-0105 - let user = 'rookie' + const users = ['rookie', 'greenhorn', 'novice', 'expert', 'oracle', 'maestro'] + let user = users[Math.floor(Math.random() * 6)] try { user = os.userInfo().username } finally { - console.log('\x1B[96m', `Hello ${user} - enjoy using ${name} ${version} !`, '\x1B[0m') + console.log('\x1B[96m', `Hello ${user} - enjoy using ${name} ${version}!`, '\x1B[0m') } } } diff --git a/src/commands/emasser/post/artifacts.ts b/src/commands/emasser/post/artifacts.ts index e4edc94f2..f6a838511 100644 --- a/src/commands/emasser/post/artifacts.ts +++ b/src/commands/emasser/post/artifacts.ts @@ -11,15 +11,32 @@ import fs, {ReadStream} from 'fs' import os from 'os' import path from 'path' +const CMD_HELP = 'saf emasser post artifacts -h or --help' export default class EmasserPostArtifacts extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command options\x1B[0m' - static description = 'Uploads [FILES] to the given [SYSTEM_ID] as artifacts' + static readonly description = 'Uploads a single or multiple artifacts to a system.\n' + + 'The single file can be an individual artifact or a .zip\n' + + 'file containing multiple artifacts. If multiple files are\n' + + 'provided they are archived into a zip file and sent as bulk.' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-i,--input] [options]'] + static readonly examples = [ + { + description: 'Add a single artifact file', + command: '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--fileName] [FLAGS]', + }, + { + description: 'Add multiple artifact files', + command: '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--fileName] ... [FLAGS]', + }, + { + description: 'Add bulk artifact file (.zip)', + command: '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--fileName] [FLAGS]', + }, + ] - static flags = { - help: Flags.help({char: 'h', description: 'Post (add) artifact file(s) to a system'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST Artifacts command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -28,21 +45,52 @@ export default class EmasserPostArtifacts extends Command { const apiCxn = new ApiConnection() const artifactApi = new ArtifactsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) - // Create the archive object, add all files - const archiver = new Zip() - flags.input.forEach((inputFile: string) => { - if (fs.existsSync(inputFile)) { - archiver.addFile(inputFile) + // Check if we have a single file, could be a zip file + if (flags.fileName.length === 1 || flags.fileName[0].endsWith('.zip')) { + if (fs.existsSync(flags.fileName[0])) { + const isBulk = Boolean(flags.fileName[0].endsWith('.zip')) + const fileStream: ReadStream = fs.createReadStream(flags.fileName[0]) + + artifactApi.addArtifactsBySystemId( + flags.systemId, fileStream, isBulk, flags.isTemplate, flags.type, flags.category).then( + (response: ArtifactsResponsePutPost) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error: any) => console.error(colorize(outputError(error)))) + } else { + console.error('\x1B[91m» Artifact file not found:', flags.fileName[0], '\x1B[0m') } - }) - // Generate zip file - const zipper = path.join(os.tmpdir(), 'zipper.zip') - await archiver.archive(zipper) - const fileStream: ReadStream = fs.createReadStream(zipper) + // Multiple files, create a zip file + } else { + // Create the archive object, add all files + const archiver = new Zip() + flags.fileName.forEach((inputFile: string) => { + if (fs.existsSync(inputFile)) { + archiver.addFile(inputFile) + } else { + console.error('\x1B[91m» Artifact file not found:', inputFile, '\x1B[0m') + process.exit(1) + } + }) + + // Generate zip file + const zipper = path.join(os.tmpdir(), 'zipper.zip') + await archiver.archive(zipper) + const fileStream: ReadStream = fs.createReadStream(zipper) + + artifactApi.addArtifactsBySystemId(flags.systemId, fileStream, true, flags.isTemplate, flags.type, flags.category).then((response: ArtifactsResponsePutPost) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + } + } - artifactApi.addArtifactsBySystemId(flags.systemId, fileStream, String(flags.isTemplate), flags.type, flags.category).then((response: ArtifactsResponsePutPost) => { - console.log(colorize(outputFormat(response, false))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } } } diff --git a/src/commands/emasser/post/cac.ts b/src/commands/emasser/post/cac.ts index d8b1bac20..f8c57313e 100644 --- a/src/commands/emasser/post/cac.ts +++ b/src/commands/emasser/post/cac.ts @@ -10,15 +10,16 @@ import {CACApi} from '@mitre/emass_client' import {CacResponsePost, CacGet as CAC} from '@mitre/emass_client/dist/api' +const CMD_HELP = 'saf emasser post cac -h or --help' export default class EmasserPostCac extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]' - static description = 'Add a Control Approval Chain (CAC) items in a system' + static readonly description = 'Add a Control Approval Chain (CAC) items in a system' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-a,--controlAcronym] [options]'] + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-a,--controlAcronym] [options]'] - static flags = { - help: Flags.help({char: 'h', description: 'Post (add) control to second stage of CAC'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST CAC command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -37,4 +38,14 @@ export default class EmasserPostCac extends Command { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/post/cloud_resources.ts b/src/commands/emasser/post/cloud_resources.ts index 42f9cfb36..c42cb1074 100644 --- a/src/commands/emasser/post/cloud_resources.ts +++ b/src/commands/emasser/post/cloud_resources.ts @@ -1,38 +1,67 @@ +/* eslint-disable valid-jsdoc */ import {colorize} from 'json-colorizer' import {Command, Flags} from '@oclif/core' import {CloudResourceResultsApi} from '@mitre/emass_client' import {CloudResourcesResponsePost} from '@mitre/emass_client/dist/api' import {ApiConnection} from '../../../utils/emasser/apiConnection' import {outputFormat} from '../../../utils/emasser/outputFormatter' -import {FlagOptions, getFlagsForEndpoint, getJsonExamples} from '../../../utils/emasser/utilities' +import {FlagOptions, getFlagsForEndpoint, getJsonExamples, printRedMsg} from '../../../utils/emasser/utilities' import {outputError} from '../../../utils/emasser/outputError' import {readFile} from 'fs/promises' import _ from 'lodash' import fs from 'fs' +/** + * Represents a cloud resource with compliance results. + * + * @interface CloudResource + * + * @property {string} provider - The cloud service provider (e.g., AWS, Azure, GCP). + * @property {string} resourceId - The unique identifier of the resource. + * @property {string} resourceName - The name of the resource. + * @property {string} resourceType - The type of the resource (e.g., VM, Storage, Database). + * @property {ComplianceResults[]} complianceResults - An array of compliance results associated with the resource. + * + * @property {string} [cspAccountId] - (Optional) The cloud service provider account ID. + * @property {string} [cspRegion] - (Optional) The region where the resource is located. + * @property {string} [initiatedBy] - (Optional) The entity or user who initiated the resource. + * @property {boolean} [isBaseline] - (Optional) Indicates if the resource is a baseline. + * @property {Tags|any} [tags] - (Optional) Tags associated with the resource. + */ interface CloudResource { + // Required provider: string, resourceId: string, resourceName: string, resourceType: string, + complianceResults: ComplianceResults[] + // Optional cspAccountId?: string, cspRegion?: string, initiatedBy?: string, isBaseline?: boolean, tags?: Tags|any, - complianceResults: ComplianceResults[] } +/** + * Represents a collection of tags where each tag is a key-value pair. + * + * @interface Tags + * @property {string} [key] - The key of the tag. + * @property {string} value - The value associated with the tag key. + */ interface Tags { [key: string]: string; } interface ComplianceResults { + // Required cspPolicyDefinitionId: string, isCompliant: boolean, policyDefinitionTitle: string, + // Optional assessmentProcedure?: string, - complianceCheckTimestamp?: string, + complianceCheckTimestamp?: number, complianceReason?: string, control?: string, policyDeploymentName?: string, @@ -40,10 +69,28 @@ interface ComplianceResults { severity?: string } -function printRedMsg(msg: string) { - console.log('\x1B[91m', msg, '\x1B[0m') +/** + * Combines JSON examples for 'cloud_resources-required' and 'cloud_resources-optional' + * into a single JSON string. + * + * @returns {string} A JSON string that merges the required and optional cloud resources examples. + */ +function getAllJsonExamples(): string { + return JSON.stringify( + _.merge({}, + getJsonExamples('cloud_resources-required'), + getJsonExamples('cloud_resources-optional'), + ), + ) } +/** + * Asserts that a required parameter exists and is not undefined. + * + * @param object - The name of the parameter or field being checked. + * @param value - The value of the parameter or field to check. Can be a string, boolean, undefined, or null. + * @throws {Error} Throws an error if the value is undefined. + */ function assertParamExists(object: string, value: string|boolean|undefined|null): void { if (value === undefined) { printRedMsg(`Missing required parameter/field: ${object}`) @@ -51,6 +98,16 @@ function assertParamExists(object: string, value: string|boolean|undefined|null) } } +/** + * Adds required fields to the request body for a CloudResource object. + * + * This function ensures that all required fields are present in the provided + * CloudResource object. If any required field is missing, an error is thrown. + * + * @param dataObj - The CloudResource object containing the data to be validated and added to the request body. + * @returns A new CloudResource object with all required fields populated. + * @throws Will throw an error if any required field is missing in the provided dataObj. + */ function addRequiredFieldsToRequestBody(dataObj: CloudResource): CloudResource { const bodyObj: CloudResource = { provider: '', @@ -99,6 +156,29 @@ function addRequiredFieldsToRequestBody(dataObj: CloudResource): CloudResource { return bodyObj } +/** + * Adds optional fields from the `dataObj` to the `bodyObject` if they exist. + * + * @param bodyObject - The target object to which optional fields will be added. + * @param dataObj - The source object containing optional fields. + * + * @remarks + * This function checks for the presence of optional fields in the `dataObj` and + * adds them to the `bodyObject` if they exist. + * It handles the following optional fields: + * - `cspAccountId` + * - `cspRegion` + * - `initiatedBy` + * - `isBaseline` + * - `tags` + * - `complianceResults` + * + * For the `tags` field, it creates a new `Tags` object and copies the key-value + * pairs from `dataObj.tags`. + * + * For the `complianceResults` field, it creates a new array of `ComplianceResults` + * objects and copies the properties from `dataObj.complianceResults`. + */ function addOptionalFields(bodyObject: CloudResource, dataObj: CloudResource): void { // Add object optional entries if (Object.prototype.hasOwnProperty.call(dataObj, 'cspAccountId')) { @@ -172,20 +252,25 @@ function addOptionalFields(bodyObject: CloudResource, dataObj: CloudResource): v bodyObject.complianceResults = complianceResultsArray } +const CMD_HELP = 'saf emasser post cloud_resources -h or --help' export default class EmasserPostCloudResources extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' - static description = 'Add a cloud resource and their scan results in the assets module for a system' + static readonly description = 'Add a cloud resource and their scan results in the assets module for a system' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--cloudResourceFile]', + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--dataFile]', 'The input file should be a well formed JSON containing the cloud resources and their scan results information.', 'Required JSON parameter/fields are: ', colorize(JSON.stringify(getJsonExamples('cloud_resources-required'), null, 2)), 'Optional JSON parameters/fields are:', - colorize(JSON.stringify(getJsonExamples('cloud_resources-optional'), null, 2))] + colorize(JSON.stringify(getJsonExamples('cloud_resources-optional'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples()), + ] - static flags = { - help: Flags.help({char: 'h', description: 'Post (add) cloud resources and their scan results in the assets module for a system'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST Cloud Resource Results command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -197,19 +282,14 @@ export default class EmasserPostCloudResources extends Command { const requestBodyArray: CloudResource[] = [] // Check if a Cloud Resource json file was provided - if (fs.existsSync(flags.cloudResourceFile)) { + if (fs.existsSync(flags.dataFile)) { let data: any try { - data = JSON.parse(await readFile(flags.cloudResourceFile, 'utf8')) + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) } catch (error: any) { - if (error.code === 'ENOENT') { - console.log('Cloud Resource JSON file not found!') - process.exit(1) - } else { - console.log('Error reading Cloud Resource file, possible malformed json. Please use the -h flag for help.') - console.log('Error message was:', error.message) - process.exit(1) - } + console.error('\x1B[91m» Error reading Cloud Resource data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) } // Create request body based on key/pair values provide in the input file @@ -250,12 +330,23 @@ export default class EmasserPostCloudResources extends Command { } } } else { - console.error('Invalid or Cloud Resource JSON file not found on the provided directory:', flags.cloudResourceFile) + console.error('\x1B[91m» Cloud Resource data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') process.exit(1) } + // Call the endpoint addCloudResource.addCloudResourcesBySystemId(flags.systemId, requestBodyArray).then((response: CloudResourcesResponsePost) => { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/post/container_scans.ts b/src/commands/emasser/post/container_scans.ts index 7a42ed3ab..de946d2f8 100644 --- a/src/commands/emasser/post/container_scans.ts +++ b/src/commands/emasser/post/container_scans.ts @@ -1,43 +1,88 @@ +/* eslint-disable valid-jsdoc */ import {colorize} from 'json-colorizer' -import {Command} from '@oclif/core' -import {ContainersApi} from '@mitre/emass_client' +import {Command, Flags} from '@oclif/core' +import {ContainerScanResultsApi} from '@mitre/emass_client' import {ContainersResponsePost} from '@mitre/emass_client/dist/api' import {ApiConnection} from '../../../utils/emasser/apiConnection' import {outputFormat} from '../../../utils/emasser/outputFormatter' -import {FlagOptions, getFlagsForEndpoint, getJsonExamples} from '../../../utils/emasser/utilities' +import {FlagOptions, getFlagsForEndpoint, getJsonExamples, printRedMsg} from '../../../utils/emasser/utilities' import {outputError} from '../../../utils/emasser/outputError' import {readFile} from 'fs/promises' import _ from 'lodash' import fs from 'fs' +/** + * Represents a container resource with associated metadata and benchmarks. + * + * @interface ContainerResource + * + * @property {string} containerId - The unique identifier of the container. (Required) + * @property {string} containerName - The name of the container. (Required) + * @property {number} time - The timestamp associated with the container resource. (Required) + * @property {Benchmarks[]} benchmarks - An array of benchmark results for the container. (Required) + * @property {string} [podName] - The name of the pod in which the container is running. (Optional) + * @property {string} [podIp] - The IP address of the pod in which the container is running. (Optional) + * @property {string} [namespace] - The namespace in which the container is running. (Optional) + * @property {Tags|any} [tags] - Additional tags or metadata associated with the container. (Optional) + */ interface ContainerResource { + // Required containerId: string, containerName: string, + time: number, + benchmarks: Benchmarks[] + // Optional podName?: string, podIp?: string, namespace?: string, - time: number, tags?: Tags|any, - benchmarks: Benchmarks[] } +/** + * Represents a collection of tags where each tag is a key-value pair. + * The key is a string representing the tag name, and the value is a string representing the tag value. + */ interface Tags { [key: string]: string; } +/** + * Represents the benchmarks for a container scan. + */ interface Benchmarks { + // Required benchmark: string, - isBaseline?: boolean, results: Results[], + // Optional + isBaseline?: boolean, + version?: string, + release?: string, } +/** + * Represents the results of a container scan. + */ interface Results { + // Required ruleId: string, status: StatusEnum, lastSeen: number, + // Optional message?: string, } +/** + * Enum representing the status of a container scan. + * + * @readonly + * @enum {string} + * @property {string} Pass - The scan passed. + * @property {string} Fail - The scan failed. + * @property {string} Other - The scan has a status other than pass or fail. + * @property {string} NotReviewed - The scan has not been reviewed. + * @property {string} NotChecked - The scan has not been checked. + * @property {string} NotApplicable - The scan is not applicable. + */ export declare const StatusEnum: { readonly Pass: 'Pass'; readonly Fail: 'Fail'; @@ -46,12 +91,36 @@ export declare const StatusEnum: { readonly NotChecked: 'Not Checked'; readonly NotApplicable: 'Not Applicable'; } +/** + * Represents the possible status values for the StatusEnum type. + * This type is derived from the keys of the StatusEnum object. + */ export declare type StatusEnum = typeof StatusEnum[keyof typeof StatusEnum] -function printRedMsg(msg: string) { - console.log('\x1B[91m', msg, '\x1B[0m') +/** + * Combines JSON examples from 'container_scans-required' and + * 'container_scans-optional' into a single JSON string. + * + * @returns {string} A JSON string that merges the required and + * optional container scan examples. + */ +function getAllJsonExamples(): string { + return JSON.stringify( + _.merge({}, + getJsonExamples('container_scans-required'), + getJsonExamples('container_scans-optional'), + ), + ) } +/** + * Asserts that a parameter exists by checking if the value is not undefined. + * If the value is undefined, it prints an error message and throws an error. + * + * @param object - The name of the parameter or field being checked. + * @param value - The value of the parameter or field which can be of type string, boolean, number, undefined, or null. + * @throws {Error} Throws an error if the value is undefined. + */ function assertParamExists(object: string, value: string|boolean|number|undefined|null): void { if (value === undefined) { printRedMsg(`Missing required parameter/field: ${object}`) @@ -59,6 +128,17 @@ function assertParamExists(object: string, value: string|boolean|number|undefine } } +/** + * Adds required fields to the request body for a container resource. + * + * This function ensures that the required fields are present in the input data object + * and constructs a new container resource object with the required fields populated. + * If any required field is missing, an error is thrown and the required fields are logged. + * + * @param {ContainerResource} dataObj - The input container resource object. + * @returns {ContainerResource} - The new container resource object with required fields populated. + * @throws Will throw an error if any required field is missing in the input data object. + */ function addRequiredFieldsToRequestBody(dataObj: ContainerResource): ContainerResource { const bodyObj: ContainerResource = { containerId: '', @@ -84,11 +164,7 @@ function addRequiredFieldsToRequestBody(dataObj: ContainerResource): ContainerRe assertParamExists(`benchmarks.results[${j}].status`, resultObj.status) j++ - const resultsObj: Results = { - ruleId: '', - status: 'Pass', - lastSeen: 0, - } + const resultsObj: Results = {ruleId: '', status: 'Pass', lastSeen: 0} resultsObj.ruleId = resultObj.ruleId resultsObj.lastSeen = resultObj.lastSeen resultsObj.status = resultObj.status @@ -118,6 +194,28 @@ function addRequiredFieldsToRequestBody(dataObj: ContainerResource): ContainerRe return bodyObj } +/** + * Adds optional fields from the `dataObj` to the `bodyObject`. + * + * @param bodyObject - The target object to which optional fields will be added. + * @param dataObj - The source object containing optional fields. + * + * The function performs the following operations: + * - Adds `namespace`, `podIp`, and `podName` fields if they exist in `dataObj`. + * - Adds `tags` object if it exists in `dataObj`. + * - Adds `benchmarks` array if it exists in `dataObj`, including optional fields within each benchmark and its results. + * + * The `benchmarks` array in `dataObj` is expected to contain objects with the following structure: + * - `benchmark` (required) + * - `isBaseline` (optional) + * - `version` (optional) + * - `release` (optional) + * - `results` (required array) containing objects with the following structure: + * - `ruleId` (required) + * - `status` (required) + * - `lastSeen` (required) + * - `message` (optional) + */ function addOptionalFields(bodyObject: ContainerResource, dataObj: ContainerResource): void { // Add object optional entries if (Object.prototype.hasOwnProperty.call(dataObj, 'namespace')) { @@ -145,30 +243,31 @@ function addOptionalFields(bodyObject: ContainerResource, dataObj: ContainerReso const resultsArray: Results[] = [] // Add the optional benchmark entries dataObj.benchmarks.forEach((entryObject: Benchmarks) => { - const benchmarksObj: Benchmarks = { - benchmark: '', - results: [], - } + const benchmarksObj: Benchmarks = {benchmark: '', results: []} // These are required benchmarksObj.benchmark = entryObject.benchmark - // Check for the optional entry + // Check for the optional entry (isBaseline, version, and release) if (Object.prototype.hasOwnProperty.call(entryObject, 'isBaseline')) { benchmarksObj.isBaseline = entryObject.isBaseline } - // Add the optional results entries + if (Object.prototype.hasOwnProperty.call(entryObject, 'version')) { + benchmarksObj.version = entryObject.version + } + + if (Object.prototype.hasOwnProperty.call(entryObject, 'release')) { + benchmarksObj.release = entryObject.release + } + + // Add the optional Results entries entryObject.results.forEach((resultObj: Results) => { - const resultsObj: Results = { - ruleId: '', - status: 'Pass', - lastSeen: 0, - } + const resultsObj: Results = {ruleId: '', status: 'Pass', lastSeen: 0} // These are required resultsObj.ruleId = resultObj.ruleId resultsObj.status = resultObj.status resultsObj.lastSeen = resultObj.lastSeen // Check for the optional entry - if (Object.prototype.hasOwnProperty.call(resultObj, 'isBaseline')) { + if (Object.prototype.hasOwnProperty.call(resultObj, 'message')) { resultsObj.message = resultObj.message } @@ -181,54 +280,50 @@ function addOptionalFields(bodyObject: ContainerResource, dataObj: ContainerReso bodyObject.benchmarks = benchmarksArray } +const CMD_HELP = 'saf emasser post container_scans -h or --help' export default class EmasserContainerScans extends Command { - static usage = '<%= command.id %> -s system-id -f container-code-scan-file.json' + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' - static description = 'Upload containers and their scan results in the assets module for a system' + static readonly description = 'Upload containers and their scan results in the assets module for a system' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--containerCodeScanFile]', + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--dataFile]', 'The input file should be a well formed JSON containing the container scan results information.', 'Required JSON parameter/fields are: ', colorize(JSON.stringify(getJsonExamples('container_scans-required'), null, 2)), 'Optional JSON parameters/fields are:', - colorize(JSON.stringify(getJsonExamples('container_scans-optional'), null, 2))] + colorize(JSON.stringify(getJsonExamples('container_scans-optional'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples()), + ] - static flags = { + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST Container Scan Results command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } async run(): Promise { const {flags} = await this.parse(EmasserContainerScans) const apiCxn = new ApiConnection() - const addContainer = new ContainersApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + const addContainer = new ContainerScanResultsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) const requestBodyArray: ContainerResource[] = [] // Check if a Cloud Resource json file was provided - if (fs.existsSync(flags.containerCodeScanFile)) { + if (fs.existsSync(flags.dataFile)) { let data: any try { - data = JSON.parse(await readFile(flags.containerCodeScanFile, 'utf8')) + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) } catch (error: any) { - if (error.code === 'ENOENT') { - console.log('Container Scan Results JSON file not found!') - process.exit(1) - } else { - console.log('Error reading Container Scan Results file, possible malformed json. Please use the -h flag for help.') - console.log('Error message was:', error.message) - process.exit(1) - } + console.error('\x1B[91m» Error reading Container Scan Results data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) } // Create request body based on key/pair values provide in the input file if (Array.isArray(data)) { data.forEach((dataObject: ContainerResource) => { - let bodyObj: ContainerResource = { - containerId: '', - containerName: '', - time: 0, - benchmarks: [], - } + let bodyObj: ContainerResource = {containerId: '', containerName: '', time: 0, benchmarks: []} // Add required fields to request array object based on business logic try { bodyObj = addRequiredFieldsToRequestBody(dataObject) @@ -240,12 +335,7 @@ export default class EmasserContainerScans extends Command { }) } else if (typeof data === 'object') { const dataObject: ContainerResource = data - let bodyObj: ContainerResource = { - containerId: '', - containerName: '', - time: 0, - benchmarks: [], - } + let bodyObj: ContainerResource = {containerId: '', containerName: '', time: 0, benchmarks: []} // Add required fields to request array object based on business logic try { bodyObj = addRequiredFieldsToRequestBody(dataObject) @@ -256,20 +346,21 @@ export default class EmasserContainerScans extends Command { } } } else { - console.error('Invalid or Container Scan Results JSON file not found on the provided directory:', flags.containerCodeScanFile) + console.error('\x1B[91m» Container Scan Results data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') process.exit(1) } + // Call the API endpoint addContainer.addContainerSansBySystemId(flags.systemId, requestBodyArray).then((response: ContainersResponsePost) => { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 - // If error message is for missing flags, display what fields - // are required, otherwise show the error + // If error message is for missing flags, display + // what fields are required, otherwise show the error if (err.message.includes('See more help with --help')) { - this.warn(err.message.replace('--help', '\x1B[93m -h or --help\x1B[0m')) + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) } else { this.warn(err) } diff --git a/src/commands/emasser/post/device_scans.ts b/src/commands/emasser/post/device_scans.ts new file mode 100644 index 000000000..02d19f5e8 --- /dev/null +++ b/src/commands/emasser/post/device_scans.ts @@ -0,0 +1,96 @@ +import {colorize} from 'json-colorizer' +import {Command, Flags} from '@oclif/core' +import {CLIError} from '@oclif/core/errors' +import {DeviceScanResultsApi} from '@mitre/emass_client' +import {DeviceScanResultsResponsePost} from '@mitre/emass_client/dist/api' +import {outputError} from '../../../utils/emasser/outputError' +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import fs from 'fs' +import path from 'path' + +const CMD_HELP = 'saf emasser post device_scans -h or --help' +export default class EmasserPostDeviceScans extends Command { + static readonly usage = '<%= command.id %> [FLAGS]' + + static readonly description = 'Add (upload) device scan results in the assets module for a system\n' + + 'Supported scan types: ACAS, DISA STIG Viewer, Policy Auditor, SCAP Compliance Checker\n' + + 'See the [-S, --scanType] command line flag for acceptable option names for scan type' + + static readonly examples = [ + { + description: 'Add a DISA STIG Viewer file (disaStigViewerCklCklb)', + command: '<%= config.bin %> <%= command.id %> [-s, --systemId] [-f, --dataFile] [-S, --scanType] [-B, --[no-]isBaseline]', + }, + { + description: 'Add an ACAS (acasAsrArf) or Policy Auditor (policyAuditor)', + command: '<%= config.bin %> <%= command.id %> [-s, --systemId] [-f, --dataFile] [-S, --scanType] [-B, --[no-]isBaseline]', + }, + { + description: 'All other supported scan types, a single file is expected', + command: '<%= config.bin %> <%= command.id %> [-s, --systemId] [-f, --dataFile] [-S, --scanType] [-B, --[no-]isBaseline]', + }, + ] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST Device Scan Results command'}), + systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), + filename: Flags.string({char: 'f', description: 'The device scan result file to be uploaded.', required: true}), + scanType: Flags.string({char: 'S', description: 'The type of scan being uploaded', required: true, + options: ['acasAsrArf', 'acasNessus', 'disaStigViewerCklCklb', 'disaStigViewerCmrs', 'policyAuditor', 'scapComplianceChecker']}), + isBaseline: Flags.boolean({char: 'B', description: 'Indicates if the scan is a baseline scan', default: false, allowNo: true}), + } + + async run(): Promise { + const {flags} = await this.parse(EmasserPostDeviceScans) + const apiCxn = new ApiConnection() + const addDeviceScans = new DeviceScanResultsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + /** + * Upload device scans (delivery method can be a file or a zip file) + * Specific file extensions are expected depending upon the scanType parameter. + * + * Scan Type File Type + * ─────────────────────────── ───────────── + * disaStigViewerCklCklb .ckl or .cklb + * acasAsrArf or policyAuditor .zip + * + * Single files are expected for all other scan types as this endpoint + * requires files to be uploaded consecutively as opposed to in bulk. + * + * Current scan types that are supported: + * ACAS: acasAsrArf or acasNessus + * DISA STIG Viewer: disaStigViewerCklCklb or disaStigViewerCmrs + * Policy Auditor: policyAuditor + * SCAP Compliance Checker: scapComplianceChecker + */ + try { + if (!fs.existsSync(flags.filename)) { + throw new CLIError(`The file ${flags.filename} does not exist. Please provide a valid file path.`) + } else if (flags.scanType === 'disaStigViewerCklCklb' && !flags.filename.includes('.ckl')) { + throw new CLIError(`If the scan type is "disaStigViewerCklCklb" a .ckl or .cklb file is expected not a ${path.extname(flags.filename)} file`) + } else if ((flags.scanType === 'acasAsrArf' || flags.scanType === 'policyAuditor') && !flags.filename.includes('.zip')) { + throw new CLIError(`If the scan type is "acasAsrArf" or "policyAuditor" a .zip file is expected not a ${path.extname(flags.filename)} file`) + } + } catch (error) { + console.error(`\x1B[91m » ${error} \x1B[0m`) + process.exit(1) + } + + const fileStream: fs.ReadStream = fs.createReadStream(flags.filename) + + addDeviceScans.addScanResultsBySystemId(flags.systemId, flags.scanType, fileStream, flags.isBaseline).then((response: DeviceScanResultsResponsePost) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error:any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } +} diff --git a/src/commands/emasser/post/hardware_baseline.ts b/src/commands/emasser/post/hardware_baseline.ts new file mode 100644 index 000000000..e221930b5 --- /dev/null +++ b/src/commands/emasser/post/hardware_baseline.ts @@ -0,0 +1,311 @@ +/* eslint-disable valid-jsdoc */ +import fs from 'fs' +import _ from 'lodash' +import {readFile} from 'fs/promises' +import {colorize} from 'json-colorizer' +import {Command, Flags} from '@oclif/core' +import { + FlagOptions, + getFlagsForEndpoint, + getJsonExamples, + printRedMsg, +} from '../../../utils/emasser/utilities' + +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import {outputError} from '../../../utils/emasser/outputError' + +import {HardwareBaselineApi} from '@mitre/emass_client' +import {HwBaselineResponsePostPut as HwBaselineResponse} from '@mitre/emass_client/dist/api' + +/** + * Represents a hardware asset with various attributes. + * + * @interface Hardware + * + * @property {string} [assetName] - The name of the asset. This is a required field. + * + * @property {string} [publicFacingFqdn] - The fully qualified domain name if the asset is public-facing. + * @property {string} [publicFacingIpAddress] - The IP address if the asset is public-facing. + * @property {string} [publicFacingUrls] - The URLs if the asset is public-facing. + * + * @property {string} [componentType] - The type of component. + * @property {string} [nickname] - A nickname for the asset. + * @property {string} [assetIpAddress] - The IP address of the asset. + * @property {boolean} [publicFacing] - Indicates if the asset is public-facing. + * @property {boolean} [virtualAsset] - Indicates if the asset is virtual. + * @property {string} [manufacturer] - The manufacturer of the asset. + * @property {string} [modelNumber] - The model number of the asset. + * @property {string} [serialNumber] - The serial number of the asset. + * @property {string} [OsIosFwVersion] - The OS/IOS/Firmware version of the asset. + * @property {string} [memorySizeType] - The memory size and type of the asset. + * @property {string} [location] - The location of the asset. + * @property {string} [approvalStatus] - The approval status of the asset. + * @property {boolean} [criticalAsset] - Indicates if the asset is critical. + */ +interface Hardware { + // Required field + assetName?: string, + // Conditional Fields + publicFacingFqdn?: string, + publicFacingIpAddress?: string, + publicFacingUrls?: string, + // Optional Fields + componentType?: string, + nickname?: string, + assetIpAddress?: string, + publicFacing?: boolean, + virtualAsset?: boolean, + manufacturer?: string, + modelNumber?: string, + serialNumber?: string, + OsIosFwVersion?: string, + memorySizeType?: string, + location?: string, + approvalStatus?: string, + criticalAsset?: boolean +} + +/** + * Combines JSON examples from multiple sources into a single object. + * + * This function aggregates JSON examples by merging the results of + * `getJsonExamples` for 'hardware-post-required', 'hardware-post-put-conditional', + * and 'hardware-post-put-optional' into one object. + * + * @returns {string} A string representation of the combined JSON examples. + */ +function getAllJsonExamples(): string { + let exampleBodyObj: any = {} + + exampleBodyObj = { + ...getJsonExamples('hardware-post-required'), + ...getJsonExamples('hardware-post-put-conditional'), + ...getJsonExamples('hardware-post-put-optional'), + } + + return exampleBodyObj +} + +/** + * Asserts that a required parameter exists and is not undefined. + * + * @param object - The name of the parameter or field being checked. + * @param value - The value of the parameter or field to check. + * @throws Will throw an error if the value is undefined. + */ +function assertParamExists(object: string, value: string|undefined|null): void { + if (value === undefined) { + printRedMsg(`Missing required parameter/field: ${object}`) + throw new Error('Value not defined') + } +} + +/** + * Adds required fields to the request body for a hardware object. + * + * This function ensures that the required fields are present in the request body. + * If the required field `assetName` is missing, an error is thrown and an example + * JSON structure is logged to the console. + * + * @param dataObj - The hardware object containing the data to be validated and added to the request body. + * @returns The hardware object with the required fields added. + * @throws Will throw an error if the required field `assetName` is missing. + */ +function addRequiredFieldsToRequestBody(dataObj: Hardware): Hardware { + const bodyObj: Hardware = {} + + try { + assertParamExists('assetName', dataObj.assetName) + } catch (error) { + console.log('Required JSON field is:') + console.log(colorize(JSON.stringify(getJsonExamples('hardware-post-required'), null, 2))) + throw error + } + + // The required parameter "systemId" is validated by oclif + bodyObj.assetName = dataObj.assetName + + return bodyObj +} + +/** + * Adds conditional fields from the `dataObj` to the `bodyObject` if they exist. + * + * @param bodyObject - The target object to which fields will be added. + * @param dataObj - The source object from which fields will be copied if they exist. + */ +function addConditionalFields(bodyObject: Hardware, dataObj: Hardware): void { + if (Object.prototype.hasOwnProperty.call(dataObj, 'publicFacingFqdn')) { + bodyObject.publicFacingFqdn = dataObj.publicFacingFqdn + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'publicFacingIpAddress')) { + bodyObject.publicFacingIpAddress = dataObj.publicFacingIpAddress + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'publicFacingUrls')) { + bodyObject.publicFacingUrls = dataObj.publicFacingUrls + } +} + +/** + * Adds optional fields from the `dataObj` to the `bodyObject` if they exist. + * + * @param bodyObject - The target object to which optional fields will be added. + * @param dataObj - The source object from which optional fields will be copied. + */ +function addOptionalFields(bodyObject: Hardware, dataObj: Hardware): void { + if (Object.prototype.hasOwnProperty.call(dataObj, 'componentType')) { + bodyObject.componentType = dataObj.componentType + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nickname')) { + bodyObject.nickname = dataObj.nickname + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'assetIpAddress')) { + bodyObject.assetIpAddress = dataObj.assetIpAddress + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'publicFacing')) { + bodyObject.publicFacing = dataObj.publicFacing + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'virtualAsset')) { + bodyObject.virtualAsset = dataObj.virtualAsset + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'manufacturer')) { + bodyObject.manufacturer = dataObj.manufacturer + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'modelNumber')) { + bodyObject.modelNumber = dataObj.modelNumber + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'serialNumber')) { + bodyObject.serialNumber = dataObj.serialNumber + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'OsIosFwVersion')) { + bodyObject.OsIosFwVersion = dataObj.OsIosFwVersion + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'memorySizeType')) { + bodyObject.memorySizeType = dataObj.memorySizeType + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'location')) { + bodyObject.location = dataObj.location + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'approvalStatus')) { + bodyObject.approvalStatus = dataObj.approvalStatus + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'criticalAsset')) { + bodyObject.criticalAsset = dataObj.criticalAsset + } +} + +/** + * Generates a body object for a hardware baseline. + * + * This function takes a `Hardware` object as input and creates a new `Hardware` object + * with required, conditional, and optional fields populated based on the input object. + * If any error occurs during the process, the function will terminate the process with an exit code of 1. + * + * @param dataObject - The input `Hardware` object containing the data to populate the body object. + * @returns The generated `Hardware` body object. + */ +function generateBodyObj(dataObject: Hardware): Hardware { + let bodyObj: Hardware = {} + + try { + bodyObj = addRequiredFieldsToRequestBody(dataObject) + addConditionalFields(bodyObj, dataObject) + addOptionalFields(bodyObj, dataObject) + } catch { + process.exit(1) + } + + return bodyObj +} + +const CMD_HELP = 'saf emasser post hardware_baseline -h or --help' +export default class EmasserHardwareBaseline extends Command { + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' + + static readonly description = 'Add one or many hardware assets to a system.\n' + + 'The CLI expects an input JSON file containing the required, conditional\n' + + 'and optional fields for the hardware asset(s) being added to the system.' + + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--dataFile]', + 'The input file should be a well formed JSON containing Hardware Assets.', + '\x1B[1mRequired JSON parameter/field is:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('hardware-post-required'), null, 2)), + '\x1B[1mConditional JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('hardware-post-put-conditional'), null, 2)), + '\x1B[1mOptional JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('hardware-post-put-optional'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples()), + ] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST Hardware Baseline command'}), + ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 + } + + async run(): Promise { + const {flags} = await this.parse(EmasserHardwareBaseline) + const apiCxn = new ApiConnection() + const hwBaseline = new HardwareBaselineApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + const requestBodyArray: Hardware[] = [] + + // Check if a Hardware json file was provided + if (fs.existsSync(flags.dataFile)) { + let data: any + try { + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) + } catch (error: any) { + console.error('\x1B[91m» Error reading Hardware data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) + } + + // Process the Hardware data file + if (Array.isArray(data)) { + data.forEach((dataObject: Hardware) => { + // Generate the post request object based on business logic + requestBodyArray.push(generateBodyObj(dataObject)) + }) + } else if (typeof data === 'object') { + const dataObject: Hardware = data + // Generate the post request object based on business logic + requestBodyArray.push(generateBodyObj(dataObject)) + } + } else { + console.error('\x1B[91m» Hardware data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') + process.exit(1) + } + + // Call the endpoint + hwBaseline.addHwBaselineAssets(flags.systemId, requestBodyArray).then((response: HwBaselineResponse) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error: any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } +} diff --git a/src/commands/emasser/post/milestones.ts b/src/commands/emasser/post/milestones.ts index e93538b21..8db6da731 100644 --- a/src/commands/emasser/post/milestones.ts +++ b/src/commands/emasser/post/milestones.ts @@ -1,5 +1,5 @@ import {colorize} from 'json-colorizer' -import {Command} from '@oclif/core' +import {Command, Flags} from '@oclif/core' import {outputError} from '../../../utils/emasser/outputError' import {ApiConnection} from '../../../utils/emasser/apiConnection' @@ -10,14 +10,18 @@ import {MilestonesApi} from '@mitre/emass_client' import {MilestoneResponsePost, MilestonesGet as Milestones} from '@mitre/emass_client/dist/api' +const CMD_HELP = 'saf emasser post milestones -h or --help' export default class EmasserPostMilestones extends Command { - static usage = '<%= command.id %> -s -p -d -c ' + static readonly usage = '<%= command.id %> -s -p -d -c ' - static description = 'Add milestones to one or many POA&M items in a system' + static readonly description = 'Add milestones to one or many POA&M items in a system\n' + + 'Milestones provide specific information about the status\n' + + 'of processes used to mitigate risks and weakness findings.\n' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-p,--poamId] [-d,--description] [-c,--scheduledCompletionDate]'] + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-p,--poamId] [-d,--description] [-c,--scheduledCompletionDate]'] - static flags = { + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST Milestones command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -29,19 +33,20 @@ export default class EmasserPostMilestones extends Command { const requestBodyArray: Milestones[] = [] requestBodyArray.push({ description: flags.description, - scheduledCompletionDate: flags.scheduledCompletionDate, + scheduledCompletionDate: Number.parseFloat(flags.scheduledCompletionDate), }) + // Call the endpoint addMilestone.addMilestoneBySystemIdAndPoamId(flags.systemId, flags.poamId, requestBodyArray).then((response: MilestoneResponsePost) => { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 - // If error message is for missing flags, display what fields - // are required, otherwise show the error + // If error message is for missing flags, display + // what fields are required, otherwise show the error if (err.message.includes('See more help with --help')) { - this.warn(err.message.replace('--help', '\x1B[93m -h or --help\x1B[0m')) + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) } else { this.warn(err) } diff --git a/src/commands/emasser/post/pac.ts b/src/commands/emasser/post/pac.ts index ef764a38b..74b719e71 100644 --- a/src/commands/emasser/post/pac.ts +++ b/src/commands/emasser/post/pac.ts @@ -10,15 +10,16 @@ import {PACApi} from '@mitre/emass_client' import {PacResponsePost, PacGet as PAC} from '@mitre/emass_client/dist/api' +const CMD_HELP = 'saf emasser post pac -h or --help' export default class EmasserPostPac extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]' - static description = 'Add new Package Approval Chain (PAC) workflow(s) for a system' + static readonly description = 'Add new Package Approval Chain (PAC) workflow(s) for a system' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-w,--workflow] [-n,--name] [-c,--comments]'] + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-w,--workflow] [-n,--name] [-c,--comments]'] - static flags = { - help: Flags.help({char: 'h', description: 'Post (add) a Package Approval Chain (PAC) item in a system'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST Package Approval Chain (PAC) command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -38,4 +39,14 @@ export default class EmasserPostPac extends Command { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/post/poams.ts b/src/commands/emasser/post/poams.ts index c034cce4d..70778d0fa 100644 --- a/src/commands/emasser/post/poams.ts +++ b/src/commands/emasser/post/poams.ts @@ -1,3 +1,4 @@ +/* eslint-disable valid-jsdoc */ import fs from 'fs' import _ from 'lodash' import {readFile} from 'fs/promises' @@ -7,20 +8,140 @@ import {Command, Flags} from '@oclif/core' import {outputError} from '../../../utils/emasser/outputError' import {ApiConnection} from '../../../utils/emasser/apiConnection' import {outputFormat} from '../../../utils/emasser/outputFormatter' -import {FlagOptions, getFlagsForEndpoint, getJsonExamples} from '../../../utils/emasser/utilities' +import {FlagOptions, getFlagsForEndpoint, getJsonExamples, printHelpMsg, printRedMsg} from '../../../utils/emasser/utilities' import {POAMApi} from '@mitre/emass_client' -import {MilestonesGet, PoamResponsePost, - PoamGet as Poams} from '@mitre/emass_client/dist/api' - -function printHelpMsg(msg: string) { - console.log('\x1B[93m', msg, '\x1B[0m') +import {MilestonesGet, PoamResponsePostPutDelete} from '@mitre/emass_client/dist/api' + +/** + * Interface representing a Plan of Action and Milestones (POAMs) object. + * + * @property {string} [status] - The current status of the POAM. + * @property {string} [vulnerabilityDescription] - Description of the vulnerability. + * @property {string} [sourceIdentifyingVulnerability] - Source identifying the vulnerability. + * @property {string} [pocOrganization] - Point of contact organization. + * @property {string} [resources] - Resources required for the POAM. + * @property {Array} [milestones] - List of milestones associated with the POAM. + * @property {string} [pocFirstName] - Point of contact first name. + * @property {string} [pocLastName] - Point of contact last name. + * @property {string} [pocEmail] - Point of contact email address. + * @property {string} [pocPhoneNumber] - Point of contact phone number. + * @property {string} [severity] - Severity level of the vulnerability. + * @property {string} [scheduledCompletionDate] - Scheduled completion date of the POAM. + * @property {string} [completionDate] - Actual completion date of the POAM. + * @property {string} [comments] - Additional comments regarding the POAM. + * @property {string} [externalUid] - External unique identifier. + * @property {string} [controlAcronym] - Control acronym associated with the POAM. + * @property {string} [assessmentProcedure] - Assessment procedure for the POAM. + * @property {string} [securityChecks] - Security checks associated with the POAM. + * @property {string} [rawSeverity] - Raw severity level of the vulnerability. + * @property {string} [relevanceOfThreat] - Relevance of the threat. + * @property {string} [likelihood] - Likelihood of the threat occurring. + * @property {string} [impact] - Impact level of the threat. + * @property {string} [impactDescription] - Description of the impact. + * @property {string} [residualRiskLevel] - Residual risk level after mitigation. + * @property {string} [recommendations] - Recommendations for mitigating the threat. + * @property {string} [mitigations] - Mitigations applied to reduce the threat. + * @property {string} [resultingResidualRiskLevelAfterProposedMitigations] - Residual risk level after proposed mitigations. + * @property {string} [predisposingConditions] - Predisposing conditions affecting the threat. + * @property {string} [threatDescription] - Description of the threat. + * @property {string} [devicesAffected] - Devices affected by the threat. + * @property {boolean} [identifiedInCFOAuditOrOtherReview] - Indicates if identified in CFO audit or other review (optional for Army and USCG, required for VA). + * @property {number} [personnelResourcesFundedBaseHours] - Funded base hours for personnel resources. + * @property {string} [personnelResourcesCostCode] - Cost code for personnel resources. + * @property {number} [personnelResourcesUnfundedBaseHours] - Unfunded base hours for personnel resources. + * @property {string} [personnelResourcesNonfundingObstacle] - Non-funding obstacle for personnel resources. + * @property {string} [personnelResourcesNonfundingObstacleOtherReason] - Other reason for non-funding obstacle for personnel resources. + * @property {number} [nonPersonnelResourcesFundedAmount] - Funded amount for non-personnel resources. + * @property {string} [nonPersonnelResourcesCostCode] - Cost code for non-personnel resources. + * @property {number} [nonPersonnelResourcesUnfundedAmount] - Unfunded amount for non-personnel resources. + * @property {string} [nonPersonnelResourcesNonfundingObstacle] - Non-funding obstacle for non-personnel resources. + * @property {string} [nonPersonnelResourcesNonfundingObstacleOtherReason] - Other reason for non-funding obstacle for non-personnel resources. + */ +interface Poams { + // Required Fields - Declared as undefined but validated later + status?: string + vulnerabilityDescription?: string + sourceIdentifyingVulnerability?: string + pocOrganization?: string + resources?: string + + // Conditional Fields + milestones?: Array + pocFirstName?: string + pocLastName?: string + pocEmail?: string + pocPhoneNumber?: string + scheduledCompletionDate?: string + completionDate?: string + comments?: string + // Conditional but certain eMASS instances may + // require the severity Risk Analysis field + severity?: string + + // Optional + externalUid?: string + controlAcronym?: string + assessmentProcedure?: string + securityChecks?: string + rawSeverity?: string + impactDescription?: string + recommendations?: string + resultingResidualRiskLevelAfterProposedMitigations?: string + predisposingConditions?: string + threatDescription?: string + devicesAffected?: string + // Optional but certain eMASS instances + // may require these Risk Analysis fields + mitigations?: string + relevanceOfThreat?: string + likelihood?: string + impact?: string + residualRiskLevel?: string + // Optional for Army and USCG - Required (Conditional) for VA + identifiedInCFOAuditOrOtherReview?: boolean + personnelResourcesFundedBaseHours?: number + personnelResourcesCostCode?: string + personnelResourcesUnfundedBaseHours?: number + personnelResourcesNonfundingObstacle?: string + personnelResourcesNonfundingObstacleOtherReason?: string + nonPersonnelResourcesFundedAmount?: number + nonPersonnelResourcesCostCode?: string + nonPersonnelResourcesUnfundedAmount?: number + nonPersonnelResourcesNonfundingObstacle?: string + nonPersonnelResourcesNonfundingObstacleOtherReason?: string } -function printRedMsg(msg: string) { - console.log('\x1B[91m', msg, '\x1B[0m') +/** + * Retrieves and combines JSON examples from various sources. + * + * This function aggregates JSON examples by merging the results of + * multiple calls to `getJsonExamples` with different parameters. + * The resulting object includes required, conditional, and optional + * examples for POAMs (Plan of Action and Milestones). + * + * @returns {string} A string representation of the combined JSON examples. + */ +function getAllJsonExamples(): string { + let exampleBodyObj: any = {} + + exampleBodyObj = { + ...getJsonExamples('poams-post-required'), + ...getJsonExamples('poams-post-put-required-va'), + ...getJsonExamples('poams-post-conditional'), + ...getJsonExamples('poams-post-put-optional'), + } + + return exampleBodyObj } +/** + * Asserts that a required parameter exists and is not undefined. + * + * @param object - The name of the parameter or field being checked. + * @param value - The value of the parameter or field to check. + * @throws Will throw an error if the value is undefined. + */ function assertParamExists(object: string, value: string|undefined|null): void { if (value === undefined) { printRedMsg(`Missing required parameter/field: ${object}`) @@ -28,32 +149,53 @@ function assertParamExists(object: string, value: string|undefined|null): void { } } +/** + * Adds required fields to the request body for a POAM (Plan of Action and Milestones) object. + * + * This function ensures that the required fields are present in the input `dataObj` and then + * constructs a new `Poams` object with these fields. If any required field is missingFields, an error + * is thrown, and a sample JSON structure of the required fields is logged. + * + * @param {Poams} dataObj - The input POAM object containing the data to be validated and added to the request body. + * @returns {Poams} - A new POAM object containing the required fields. + * @throws Will throw an error if any of the required fields are missingFields in the input `dataObj`. + */ function addRequiredFieldsToRequestBody(dataObj: Poams): Poams { const bodyObj: Poams = {} try { assertParamExists('status', dataObj.status) assertParamExists('vulnerabilityDescription', dataObj.vulnerabilityDescription) - assertParamExists('sourceIdentVuln', dataObj.sourceIdentVuln) + assertParamExists('sourceIdentifyingVulnerability', dataObj.sourceIdentifyingVulnerability) assertParamExists('pocOrganization', dataObj.pocOrganization) assertParamExists('resources', dataObj.resources) - assertParamExists('mitigation', dataObj.mitigation) } catch (error) { console.log('Required JSON fields are:') console.log(colorize(JSON.stringify(getJsonExamples('poams-post-required'), null, 2))) throw error } - // The required parameters/fields "poamId" and "displayPoamId" are generated by the POST call + // The required parameter "systemId" is validated by oclif bodyObj.status = dataObj.status bodyObj.vulnerabilityDescription = dataObj.vulnerabilityDescription - bodyObj.sourceIdentVuln = dataObj.sourceIdentVuln + bodyObj.sourceIdentifyingVulnerability = dataObj.sourceIdentifyingVulnerability bodyObj.pocOrganization = dataObj.pocOrganization bodyObj.resources = dataObj.resources - bodyObj.mitigation = dataObj.mitigation return bodyObj } +/** + * Adds conditional fields from the `dataObj` to the `bodyObject` if they exist. + * + * @remarks + * This function checks if the properties `pocFirstName`, `pocLastName`, `pocEmail`, + * `pocPhoneNumber`, `severity` + * exist in the `dataObj`. If they do, it assigns their values to the corresponding properties + * in the `bodyObject`. + * + * The properties `scheduledCompletionDate`, `completionDate`, and `comments` are checked + * in the business logic function. + */ function addConditionalFields(bodyObject: Poams, dataObj: Poams): void { if (Object.prototype.hasOwnProperty.call(dataObj, 'pocFirstName')) { bodyObject.pocFirstName = dataObj.pocFirstName @@ -76,6 +218,13 @@ function addConditionalFields(bodyObject: Poams, dataObj: Poams): void { } } +/** + * Adds optional fields from the data object to the body object if they exist. + * + * @param bodyObject - The target object to which optional fields will be added. + * @param dataObj - The source object containing optional fields. + */ +// skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold function addOptionalFields(bodyObject: Poams, dataObj: Poams): void { if (Object.prototype.hasOwnProperty.call(dataObj, 'externalUid')) { bodyObject.externalUid = dataObj.externalUid @@ -85,8 +234,8 @@ function addOptionalFields(bodyObject: Poams, dataObj: Poams): void { bodyObject.controlAcronym = dataObj.controlAcronym } - if (Object.prototype.hasOwnProperty.call(dataObj, 'cci')) { - bodyObject.cci = dataObj.cci + if (Object.prototype.hasOwnProperty.call(dataObj, 'assessmentProcedure')) { + bodyObject.assessmentProcedure = dataObj.assessmentProcedure } if (Object.prototype.hasOwnProperty.call(dataObj, 'securityChecks')) { @@ -120,28 +269,117 @@ function addOptionalFields(bodyObject: Poams, dataObj: Poams): void { if (Object.prototype.hasOwnProperty.call(dataObj, 'recommendations')) { bodyObject.recommendations = dataObj.recommendations } - // if (Object.prototype.hasOwnProperty.call(dataObj, 'mitigation')) {bodyObject.mitigation = dataObj.mitigation } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'mitigations')) { + bodyObject.mitigations = dataObj.mitigations + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'resultingResidualRiskLevelAfterProposedMitigations')) { + bodyObject.resultingResidualRiskLevelAfterProposedMitigations = dataObj.resultingResidualRiskLevelAfterProposedMitigations + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'predisposingConditions')) { + bodyObject.predisposingConditions = dataObj.predisposingConditions + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'threatDescription')) { + bodyObject.threatDescription = dataObj.threatDescription + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'devicesAffected')) { + bodyObject.devicesAffected = dataObj.devicesAffected + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'identifiedInCFOAuditOrOtherReview')) { + bodyObject.identifiedInCFOAuditOrOtherReview = dataObj.identifiedInCFOAuditOrOtherReview + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'personnelResourcesFundedBaseHours')) { + bodyObject.personnelResourcesFundedBaseHours = dataObj.personnelResourcesFundedBaseHours + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'personnelResourcesCostCode')) { + bodyObject.personnelResourcesCostCode = dataObj.personnelResourcesCostCode + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'personnelResourcesUnfundedBaseHours')) { + bodyObject.personnelResourcesUnfundedBaseHours = dataObj.personnelResourcesUnfundedBaseHours + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'personnelResourcesNonfundingObstacle')) { + bodyObject.personnelResourcesNonfundingObstacle = dataObj.personnelResourcesNonfundingObstacle + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'personnelResourcesNonfundingObstacleOtherReason')) { + bodyObject.personnelResourcesNonfundingObstacleOtherReason = dataObj.personnelResourcesNonfundingObstacleOtherReason + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nonPersonnelResourcesFundedAmount')) { + bodyObject.nonPersonnelResourcesFundedAmount = dataObj.nonPersonnelResourcesFundedAmount + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nonPersonnelResourcesCostCode')) { + bodyObject.nonPersonnelResourcesCostCode = dataObj.nonPersonnelResourcesCostCode + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nonPersonnelResourcesUnfundedAmount')) { + bodyObject.nonPersonnelResourcesUnfundedAmount = dataObj.nonPersonnelResourcesUnfundedAmount + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nonPersonnelResourcesNonfundingObstacle')) { + bodyObject.nonPersonnelResourcesNonfundingObstacle = dataObj.nonPersonnelResourcesNonfundingObstacle + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nonPersonnelResourcesNonfundingObstacleOtherReason')) { + bodyObject.nonPersonnelResourcesNonfundingObstacleOtherReason = dataObj.nonPersonnelResourcesNonfundingObstacleOtherReason + } } +/** + * Processes the business logic for POA&M (Plan of Action and Milestones) based on the status field value. + * + * @param bodyObject - The object that will be populated with the required fields based on the status. + * @param dataObj - The input object containing the status and other fields to be validated and processed. + * + * The function performs the following checks and operations based on the status: + * + * - "Risk Accepted": + * - Requires `comments` field. + * - Ensures `scheduledCompletionDate` and `milestones` fields are not present. + * + * - "Ongoing": + * - Requires `scheduledCompletionDate` and `milestones` fields. + * - Ensures at least one milestone object has `description` and `scheduledCompletionDate`. + * + * - "Completed": + * - Requires `scheduledCompletionDate`, `comments`, `completionDate`, and `milestones` fields. + * + * - "Not Applicable": + * - POAMs cannot be created with this status. + * + * + * If any required fields are missingFields or invalid, the function prints an error message and exits the process. + * + * Additionally, if any POC (Point of Contact) information is provided, all POC fields (`pocFirstName`, `pocLastName`, `pocEmail`, `pocPhoneNumber`) are required. + * + * @remarks + * The function uses `printRedMsg` to display error messages and `printHelpMsg` to display help messages. + * It exits the process with a status code of 1 if any validation fails. + */ +// skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold function processBusinessLogic(bodyObject: Poams, dataObj: Poams): void { // skipcq: JS-0044 - //----------------------------------------------------------------------------- - // Conditional fields that are required based on the "status" field value - // "Risk Accepted" comments, resources - // "Ongoing" scheduledCompletionDate, resources, milestones (at least 1) - // "Completed" scheduledCompletionDate, comments, resources, - // completionDate, milestones (at least 1) - // "Not Applicable" POAM can not be created - //----------------------------------------------------------------------------- - const HELP_MSG = '\nInvoke saf emasser post poams [-h, --help] for additional help' + const HELP_MSG = 'Invoke saf emasser post poams [-h, --help] for additional help' switch (dataObj.status) { case 'Risk Accepted': { + // Risk Accepted POA&M Item require a comments field if (dataObj.comments === undefined) { - printRedMsg('When status is "Risk Accepted" the following parameters/fields are required:') + printRedMsg('When status is "Risk Accepted" the following parameter/field is required:') printRedMsg(' comments') printHelpMsg(HELP_MSG) process.exit(1) + // Risk Accepted POA&M Item cannot be saved with a Scheduled Completion Date or Milestones. } else if (Object.prototype.hasOwnProperty.call(dataObj, 'scheduledCompletionDate') || Object.prototype.hasOwnProperty.call(dataObj, 'milestones')) { - printRedMsg('When status is "Risk Accepted" POA&Ms CAN NOT be saved with the following parameters/field:') + printRedMsg('When status is "Risk Accepted" POA&Ms CAN NOT be saved with the following parameters/fields:') printRedMsg(' scheduledCompletionDate, or milestones') printHelpMsg(HELP_MSG) process.exit(1) @@ -153,19 +391,16 @@ function processBusinessLogic(bodyObject: Poams, dataObj: Poams): void { // skip } case 'Ongoing': { + // POA&M Items that have a status of “Ongoing” cannot be saved without Milestones or Scheduled Completion. if (!(Object.prototype.hasOwnProperty.call(dataObj, 'scheduledCompletionDate') && Object.prototype.hasOwnProperty.call(dataObj, 'milestones'))) { printRedMsg('When status is "Ongoing" the following parameters/fields are required:') printRedMsg(' scheduledCompletionDate, milestones') printHelpMsg(HELP_MSG) process.exit(1) - } else if (((_.some(dataObj.milestones, function (milestone) { // skipcq: JS-0241 - return milestone.description - }))) || - ((_.some(dataObj.milestones, function (milestone) { // skipcq: JS-0241 - return milestone.scheduledCompletionDate - })))) { - printRedMsg('At least one milestone parameters/fields object must be defined:') - printRedMsg(' "milestones": [{"description": "The milestone description", "scheduledCompletionDate": 1637342288 }], ') + // If we have a milestone, ensure the required fields are provided. + } else if (!(_.some(dataObj.milestones, 'description')) || !(_.some(dataObj.milestones, 'scheduledCompletionDate'))) { + printRedMsg('Milestone object requires the following fields:') + printRedMsg(' "milestones": [{"description": "The milestone description", "scheduledCompletionDate": Unix date format }], ') process.exit(1) } else { // Add the POA&M completion date @@ -186,6 +421,8 @@ function processBusinessLogic(bodyObject: Poams, dataObj: Poams): void { // skip } case 'Completed': { + // Completed POA&M Item require the completionDate, comments, and Milestones. + // Given that this is a POST and the POA&M is completed, the scheduledCompletionDate is acceptable if (!(Object.prototype.hasOwnProperty.call(dataObj, 'scheduledCompletionDate')) || !(Object.prototype.hasOwnProperty.call(dataObj, 'comments')) || !(Object.prototype.hasOwnProperty.call(dataObj, 'completionDate')) || !(Object.prototype.hasOwnProperty.call(dataObj, 'milestones'))) { printRedMsg('When status is "Completed" the following parameters/fields are required:') @@ -212,6 +449,13 @@ function processBusinessLogic(bodyObject: Poams, dataObj: Poams): void { // skip break } + case 'Not Applicable': { + printRedMsg('POA&M Item cannot be created manually if a security Control or Assessment Procedure is "Not Applicable".') + process.exit(1) + + break + } + default: { printRedMsg('The "status" field must one of the following:') printRedMsg(' Risk Accepted, Ongoing, or Completed') @@ -222,12 +466,15 @@ function processBusinessLogic(bodyObject: Poams, dataObj: Poams): void { // skip } // POC checks: If any poc information is provided all POC fields are required - if ((Object.prototype.hasOwnProperty.call(dataObj, 'pocFirstName') || Object.prototype.hasOwnProperty.call(dataObj, 'pocLastName') || - Object.prototype.hasOwnProperty.call(dataObj, 'pocEmail') || Object.prototype.hasOwnProperty.call(dataObj, 'pocPhoneNumber')) && ( - !(Object.prototype.hasOwnProperty.call(dataObj, 'pocFirstName')) || !(Object.prototype.hasOwnProperty.call(dataObj, 'pocLastName')) || - !(Object.prototype.hasOwnProperty.call(dataObj, 'pocEmail')) || !(Object.prototype.hasOwnProperty.call(dataObj, 'pocPhoneNumber')))) { - printRedMsg('If any POC content is provided (pocFirstName, pocLastName, pocEmail, pocPhoneNumber) than all POC content is required:') - printRedMsg(' pocFirstName, pocLastName, pocEmail, pocPhoneNumber') + let missingFields = '' + if ((_.get(dataObj, 'pocFirstName') === undefined)) missingFields = 'pocFirstName' + if ((_.get(dataObj, 'pocLastName') === undefined)) missingFields += (missingFields === '') ? 'pocLastName' : ', pocLastName' + if ((_.get(dataObj, 'pocEmail') === undefined)) missingFields += (missingFields === '') ? 'pocEmail' : ', pocEmail' + if ((_.get(dataObj, 'pocPhoneNumber') === undefined)) missingFields += (missingFields === '') ? 'pocPhoneNumber' : ', pocPhoneNumber' + const totalPocMissingFields = missingFields.split(',').length + if ((totalPocMissingFields >= 1 && totalPocMissingFields < 4) && missingFields !== '') { + printRedMsg('If any POC fields are provided (pocFirstName, pocLastName, pocEmail, pocPhoneNumber) than all POC fields are required:') + printRedMsg(` Missing field(s): ${missingFields}`) printHelpMsg(HELP_MSG) process.exit(1) } @@ -247,22 +494,31 @@ function generateBodyObj(dataObject: Poams): Poams { return bodyObj } +const CMD_HELP = 'saf emasser post poams -h or --help' export default class EmasserPostPoams extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' - static description = 'Add a Plan of Action and Milestones (POA&M) into a systems.' + static readonly description = 'Add a Plan of Action and Milestones (POA&M) into a systems.\n' + + 'This CLI expects an input file containing the necessary fields to add a POA&M. The content\n' + + 'of the file must be in compliance with the eMASS API defined business rules for adding POA&Ms.' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--poamFile]', + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--dataFile]', 'The input file should be a well formed JSON containing the POA&M information based on defined business rules.', - 'Required JSON parameter/fields are: ', + '\x1B[1mRequired JSON parameter/fields are:\x1B[0m', colorize(JSON.stringify(getJsonExamples('poams-post-required'), null, 2)), - 'Conditional JSON parameters/fields are: ', + '\x1B[1mRequired for VA but Conditional for Army and USCG JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('poams-post-put-required-va'), null, 2)), + '\x1B[1mConditional JSON parameters/fields are:\x1B[0m', colorize(JSON.stringify(getJsonExamples('poams-post-conditional'), null, 2)), - 'Optional JSON parameters/fields are:', - colorize(JSON.stringify(getJsonExamples('poams-post-put-optional'), null, 2))] - - static flags = { - help: Flags.help({char: 'h', description: 'Post (add) a Plan of Action and Milestones (POA&M) item(s) in a system. See emasser Features (emasserFeatures.md) for additional information.'}), + '\x1B[1mOptional JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('poams-post-put-optional'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples()), + ] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST POA&Ms command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -274,19 +530,14 @@ export default class EmasserPostPoams extends Command { const requestBodyArray: Poams[] = [] // Check if a POA&Ms json file was provided - if (fs.existsSync(flags.poamFile)) { + if (fs.existsSync(flags.dataFile)) { let data: any try { - data = JSON.parse(await readFile(flags.poamFile, 'utf8')) + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) } catch (error: any) { - if (error.code === 'ENOENT') { - console.log('POA&Ms JSON file not found!') - process.exit(1) - } else { - console.log('Error reading POA&Ms file, possible malformed json. Please use the -h flag for help.') - console.log('Error message was:', error.message) - process.exit(1) - } + console.error('\x1B[91m» Error reading POA&Ms data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) } // POA&Ms json file provided, check if we have multiple POA&Ms to process @@ -301,12 +552,23 @@ export default class EmasserPostPoams extends Command { requestBodyArray.push(generateBodyObj(dataObject)) } } else { - console.error('Invalid or POA&M JSON file not found on the provided directory:', flags.poamFile) + console.error('\x1B[91m» POA&M(s) data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') process.exit(1) } - addPoam.addPoamBySystemId(flags.systemId, requestBodyArray).then((response: PoamResponsePost) => { + // Call the endpoint + addPoam.addPoamBySystemId(flags.systemId, requestBodyArray).then((response: PoamResponsePostPutDelete) => { console.log(colorize(outputFormat(response, false))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } } } diff --git a/src/commands/emasser/post/register.ts b/src/commands/emasser/post/register.ts index 8333d973a..9d2a70750 100644 --- a/src/commands/emasser/post/register.ts +++ b/src/commands/emasser/post/register.ts @@ -7,22 +7,29 @@ import {outputFormat} from '../../../utils/emasser/outputFormatter' import {outputError} from '../../../utils/emasser/outputError' export default class EmasserPostRegister extends Command { - static usage = '<%= command.id %>' + static readonly usage = '<%= command.id %>' - static description = 'The Registration endpoint provides the ability to register a certificate & obtain an API-key' + static readonly description = 'The Registration endpoint provides the ability to register a certificate & obtain an API-key' - static examples = ['<%= config.bin %> <%= command.id %>'] + static readonly examples = ['<%= config.bin %> <%= command.id %>'] - static flags = { - help: Flags.help({char: 'h', description: 'Register a certificate & obtain the API-key - The certificate is provided in the environment configuration file (.env)'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the Register (POST) a certificate & obtain the API-key'}), } async run(): Promise { // skipcq: JS-0116, JS-0105 const apiCxn = new ApiConnection() + const headers = { + 'Content-Type': 'application/json', + Accept: 'application/json', + } + apiCxn.axiosInstances.defaults.headers.common = headers const registerAPI = new RegistrationApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) registerAPI.registerUser().then((response: Register) => { console.log(colorize(outputFormat(response, false))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => { + console.error(colorize(outputError(error)).red) + }) } } diff --git a/src/commands/emasser/post/software_baseline.ts b/src/commands/emasser/post/software_baseline.ts new file mode 100644 index 000000000..009047e66 --- /dev/null +++ b/src/commands/emasser/post/software_baseline.ts @@ -0,0 +1,413 @@ +/* eslint-disable valid-jsdoc */ +import fs from 'fs' +import _ from 'lodash' +import {readFile} from 'fs/promises' +import {colorize} from 'json-colorizer' +import {Command, Flags} from '@oclif/core' +import {FlagOptions, getFlagsForEndpoint, getJsonExamples, printRedMsg} from '../../../utils/emasser/utilities' +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {SoftwareBaselineApi} from '@mitre/emass_client' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import {outputError} from '../../../utils/emasser/outputError' +import {SwBaselineResponsePostPut} from '@mitre/emass_client/dist/api' + +/** + * Interface representing software details. + * + * @property {string} [softwareVendor] - The vendor of the software. (Required) + * @property {string} [softwareName] - The name of the software. (Required) + * @property {string} [version] - The version of the software. (Required) + * @property {number} [approvalDate] - The date the software was approved. (Conditional) + * @property {string} [softwareType] - The type of the software. (Optional) + * @property {string} [parentSystem] - The parent system of the software. (Optional) + * @property {string} [subsystem] - The subsystem of the software. (Optional) + * @property {string} [network] - The network the software is part of. (Optional) + * @property {string} [hostingEnvironment] - The hosting environment of the software. (Optional) + * @property {string} [softwareDependencies] - The dependencies of the software. (Optional) + * @property {string} [cryptographicHash] - The cryptographic hash of the software. (Optional) + * @property {string} [inServiceData] - The in-service data of the software. (Optional) + * @property {string} [itBudgetUii] - The IT budget UII of the software. (Optional) + * @property {string} [fiscalYear] - The fiscal year of the software. (Optional) + * @property {string} [popEndDate] - The end date of the period of performance. (Optional) + * @property {string} [licenseOrContract] - The license or contract details of the software. (Optional) + * @property {string} [licenseTerm] - The term of the license. (Optional) + * @property {number} [costPerLicense] - The cost per license. (Optional) + * @property {number} [totalLicenses] - The total number of licenses. (Optional) + * @property {number} [totalLicenseCost] - The total cost of licenses. (Optional) + * @property {number} [licensesUsed] - The number of licenses used. (Optional) + * @property {string} [licensePoc] - The point of contact for the license. (Optional) + * @property {number} [licenseRenewalDate] - The date the license needs to be renewed. (Optional) + * @property {number} [licenseExpirationDate] - The expiration date of the license. (Optional) + * @property {string} [approvalStatus] - The approval status of the software. (Optional) + * @property {number} [releaseDate] - The release date of the software. (Optional) + * @property {number} [maintenanceDate] - The maintenance date of the software. (Optional) + * @property {number} [retirementDate] - The retirement date of the software. (Optional) + * @property {number} [endOfLifeSupportDate] - The end-of-life support date of the software. (Optional) + * @property {number} [extendedEndOfLifeSupportDate] - The extended end-of-life support date of the software. (Optional) + * @property {boolean} [criticalAsset] - Indicates if the software is a critical asset. (Optional) + * @property {string} [location] - The location of the software. (Optional) + * @property {string} [purpose] - The purpose of the software. (Optional) + * @property {boolean} [unsupportedOperatingSystem] - Indicates if the software is running on an unsupported operating system. (VA Only) + * @property {boolean} [unapprovedSoftwareFromTrm] - Indicates if the software is unapproved from TRM. (VA Only) + * @property {boolean} [approvedWaiver] - Indicates if there is an approved waiver for the software. (VA Only) + */ +interface Software { + // Required field + softwareVendor?: string, + softwareName?: string, + version?: string, + // Conditional Fields + approvalDate?: number, + // Optional Fields + softwareType?: string, + parentSystem?: string, + subsystem?: string, + network?: string, + hostingEnvironment?: string, + softwareDependencies?: string, + cryptographicHash?: string, + inServiceData?: string, + itBudgetUii?: string, + fiscalYear?: string, + popEndDate?:string, + licenseOrContract?: string, + licenseTerm?: string, + costPerLicense?: number, + totalLicenses?:number, + totalLicenseCost?:number, + licensesUsed?:number, + licensePoc?:string, + licenseRenewalDate?: number, + licenseExpirationDate?: number, + approvalStatus?: string, + releaseDate?: number, + maintenanceDate?: number, + retirementDate?: number, + endOfLifeSupportDate?: number, + extendedEndOfLifeSupportDate?: number, + criticalAsset?: boolean, + location?:string, + purpose?:string, + // VA Only + unsupportedOperatingSystem?: boolean, + unapprovedSoftwareFromTrm?: boolean, + approvedWaiver?: boolean, +} + +/** + * Combines SON examples from multiple sources into a single obect. + * + * This function aggregates JSON examples by merging the results of + * `getJsonExamples` for 'software-post-required', 'software-post-put-conditional', + * and 'software-post-put-optional' into one object. + * + * @returns {string} A string representation of the combined JSON examples. + */ +function getAllJsonExamples(): string { + let exampleBodyObj: any = {} + + exampleBodyObj = { + ...getJsonExamples('software-post-required'), + ...getJsonExamples('software-post-put-conditional'), + ...getJsonExamples('software-post-put-optional'), + } + + return exampleBodyObj +} + +/** + * Asserts that a required parameter exists and is not undefined. + * + * @param object - The name of the parameter or field being checked. + * @param value - The value of the parameter or field to check. + * @throws Will throw an error if the value is undefined. + */ +function assertParamExists(object: string, value: string|undefined|null): void { + if (value === undefined) { + printRedMsg(`Missing required parameter/field: ${object}`) + throw new Error('Value not defined') + } +} + +/** + * Adds required fields to the request body for a software baseline. + * + * This function ensures that the required fields `softwareVendor`, `softwareName`, and `version` + * are present in the provided `dataObj`. If any of these fields are missing, an error is thrown + * and an example JSON structure is logged to the console. + * + * @param dataObj - The software object containing the data to be validated and added to the request body. + * @returns A new `Software` object containing only the required fields. + * @throws Will throw an error if any of the required fields are missing in `dataObj`. + */ +function addRequiredFieldsToRequestBody(dataObj: Software): Software { + const bodyObj: Software = {} + + try { + assertParamExists('softwareVendor', dataObj.softwareVendor) + assertParamExists('softwareName', dataObj.softwareName) + assertParamExists('version', dataObj.version) + } catch (error) { + console.log('Required JSON fields are:') + console.log(colorize(JSON.stringify(getJsonExamples('software-post-required'), null, 2))) + throw error + } + + // The required parameter "systemId" is validated by oclif + bodyObj.softwareVendor = dataObj.softwareVendor + bodyObj.softwareName = dataObj.softwareName + bodyObj.version = dataObj.version + + return bodyObj +} + +/** + * Adds conditional fields from the `dataObj` to the `bodyObject` if they exist. + * + * @param bodyObject - The target object to which fields will be added. + * @param dataObj - The source object from which fields will be copied if they exist. + */ +function addConditionalFields(bodyObject: Software, dataObj: Software): void { + if (Object.prototype.hasOwnProperty.call(dataObj, 'publicFacingFqdn')) { + bodyObject.approvalDate = dataObj.approvalDate + } +} + +/** + * Adds optional fields from the `dataObj` to the `bodyObject` if they exist. + * + * @param bodyObject - The target object to which optional fields will be added. + * @param dataObj - The source object from which optional fields will be copied. + */ +// skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold +function addOptionalFields(bodyObject: Software, dataObj: Software): void { + if (Object.prototype.hasOwnProperty.call(dataObj, 'softwareType')) { + bodyObject.softwareType = dataObj.softwareType + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'parentSystem')) { + bodyObject.parentSystem = dataObj.parentSystem + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'subsystem')) { + bodyObject.subsystem = dataObj.subsystem + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'network')) { + bodyObject.network = dataObj.network + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'hostingEnvironment')) { + bodyObject.hostingEnvironment = dataObj.hostingEnvironment + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'softwareDependencies')) { + bodyObject.softwareDependencies = dataObj.softwareDependencies + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'cryptographicHash')) { + bodyObject.cryptographicHash = dataObj.cryptographicHash + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'inServiceData')) { + bodyObject.inServiceData = dataObj.inServiceData + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'itBudgetUii')) { + bodyObject.itBudgetUii = dataObj.itBudgetUii + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'fiscalYear')) { + bodyObject.fiscalYear = dataObj.fiscalYear + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'popEndDate')) { + bodyObject.popEndDate = dataObj.popEndDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licenseOrContract')) { + bodyObject.licenseOrContract = dataObj.licenseOrContract + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licenseTerm')) { + bodyObject.licenseTerm = dataObj.licenseTerm + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'costPerLicense')) { + bodyObject.costPerLicense = dataObj.costPerLicense + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'totalLicenses')) { + bodyObject.totalLicenses = dataObj.totalLicenses + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'totalLicenseCost')) { + bodyObject.totalLicenseCost = dataObj.totalLicenseCost + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licensesUsed')) { + bodyObject.licensesUsed = dataObj.licensesUsed + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licensePoc')) { + bodyObject.licensePoc = dataObj.licensePoc + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licenseRenewalDate')) { + bodyObject.licenseRenewalDate = dataObj.licenseRenewalDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licenseExpirationDate ')) { + bodyObject.licenseExpirationDate = dataObj.licenseExpirationDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'approvalStatus')) { + bodyObject.approvalStatus = dataObj.approvalStatus + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'releaseDate')) { + bodyObject.releaseDate = dataObj.releaseDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'maintenanceDate')) { + bodyObject.maintenanceDate = dataObj.maintenanceDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'retirementDate')) { + bodyObject.retirementDate = dataObj.retirementDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'endOfLifeSupportDate')) { + bodyObject.endOfLifeSupportDate = dataObj.endOfLifeSupportDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'extendedEndOfLifeSupportDate')) { + bodyObject.extendedEndOfLifeSupportDate = dataObj.extendedEndOfLifeSupportDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'criticalAsset')) { + bodyObject.criticalAsset = dataObj.criticalAsset + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'location')) { + bodyObject.location = dataObj.location + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'purpose')) { + bodyObject.purpose = dataObj.purpose + } + + // VA Only + if (Object.prototype.hasOwnProperty.call(dataObj, 'unsupportedOperatingSystem')) { + bodyObject.unsupportedOperatingSystem = dataObj.unsupportedOperatingSystem + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'unapprovedSoftwareFromTrm')) { + bodyObject.unapprovedSoftwareFromTrm = dataObj.unapprovedSoftwareFromTrm + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'approvedWaiver')) { + bodyObject.approvedWaiver = dataObj.approvedWaiver + } +} + +/** + * Generates a body object for a software baseline. + * + * This function takes a `Software` object as input and creates a new `Software` object + * with required, conditional, and optional fields populated based on the input object. + * If any error occurs during the process, the function will terminate the process with an exit code of 1. + * + * @param dataObject - The input `Software` object containing the data to populate the body object. + * @returns The generated `Software` body object. + */ +function generateBodyObj(dataObject: Software): Software { + let bodyObj: Software = {} + + try { + bodyObj = addRequiredFieldsToRequestBody(dataObject) + addConditionalFields(bodyObj, dataObject) + addOptionalFields(bodyObj, dataObject) + } catch { + process.exit(1) + } + + return bodyObj +} + +const CMD_HELP = 'saf emasser post software_baseline -h or --help' +export default class EmasserSoftwareBaseline extends Command { + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' + + static readonly description = 'Add one or many software assets to a system.\n' + + 'The CLI expects an input JSON file containing the required, conditional\n' + + 'and optional fields for the software asset(s) being added to the system.' + + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--dataFile]', + 'The input file should be a well formed JSON containing Software Assets.', + '\x1B[1mRequired JSON parameter/field is:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('software-post-required'), null, 2)), + '\x1B[1mConditional JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('software-post-put-conditional'), null, 2)), + '\x1B[1mOptional JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('software-post-put-optional'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples()), + ] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST Software Baseline command'}), + ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 + } + + async run(): Promise { + const {flags} = await this.parse(EmasserSoftwareBaseline) + const apiCxn = new ApiConnection() + const swBaseline = new SoftwareBaselineApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + const requestBodyArray: Software[] = [] + + // Check if a Software json file was provided + if (fs.existsSync(flags.dataFile)) { + let data: any + try { + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) + } catch (error: any) { + console.error('\x1B[91m» Error reading Software data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) + } + + // Process the Software data file + if (Array.isArray(data)) { + data.forEach((dataObject: Software) => { + // Generate the post request object based on business logic + requestBodyArray.push(generateBodyObj(dataObject)) + }) + } else if (typeof data === 'object') { + const dataObject: Software = data + // Generate the post request object based on business logic + requestBodyArray.push(generateBodyObj(dataObject)) + } + } else { + console.error('\x1B[91m» Software data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') + process.exit(1) + } + + // Call the endpoint + swBaseline.addSwBaselineAssets(flags.systemId, requestBodyArray).then((response: SwBaselineResponsePostPut) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error: any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } +} diff --git a/src/commands/emasser/post/static_code_scans.ts b/src/commands/emasser/post/static_code_scans.ts index 9c872cbae..94fb2b52f 100644 --- a/src/commands/emasser/post/static_code_scans.ts +++ b/src/commands/emasser/post/static_code_scans.ts @@ -1,3 +1,4 @@ +/* eslint-disable valid-jsdoc */ import fs from 'fs' import _ from 'lodash' import {readFile} from 'fs/promises' @@ -7,17 +8,52 @@ import {Command, Flags} from '@oclif/core' import {outputError} from '../../../utils/emasser/outputError' import {ApiConnection} from '../../../utils/emasser/apiConnection' import {outputFormat} from '../../../utils/emasser/outputFormatter' -import {FlagOptions, getFlagsForEndpoint, getJsonExamples} from '../../../utils/emasser/utilities' +import {FlagOptions, getFlagsForEndpoint, getJsonExamples, printRedMsg} from '../../../utils/emasser/utilities' import {StaticCodeScansApi} from '@mitre/emass_client' -import {StaticCodeApplication, StaticCodeResponsePost, +import {StaticCodeApplicationPost, StaticCodeResponsePost, StaticCodeRequestPostBody as StaticCodeRequest, StaticCodeRequestPostBodyApplication as ApplicationRequestBody} from '@mitre/emass_client/dist/api' -function printRedMsg(msg: string) { - console.log('\x1B[91m', msg, '\x1B[0m') +/** + * Generates a JSON string based on the provided action. + * + * @param action - The action to determine which JSON examples to merge. + * Valid values are 'add' and 'clear'. + * - 'add': Merges 'scan_findings-application' and 'scan_findings-applicationFindings'. + * - 'clear': Merges 'scan_findings-application' and 'scan_findings-clearFindings'. + * @returns A JSON string representing the merged examples for the specified action. + * Returns an empty string if the action is not recognized. + */ +function getAllJsonExamples(action: string): string { + if (action === 'add') { + return JSON.stringify( + _.merge({}, + getJsonExamples('scan_findings-application'), + getJsonExamples('scan_findings-applicationFindings'), + ), + ) + } + + if (action === 'clear') { + return JSON.stringify( + _.merge({}, + getJsonExamples('scan_findings-application'), + getJsonExamples('scan_findings-clearFindings'), + ), + ) + } + + return '' } +/** + * Asserts that a parameter exists and is not undefined. + * + * @param {string} object - The name of the parameter or field being checked. + * @param {string | boolean | number | undefined | null} value - The value of the parameter or field to check. + * @throws {Error} Throws an error if the value is undefined. + */ function assertParamExists(object: string, value: string|boolean|number|undefined|null): void { if (value === undefined) { printRedMsg(`Missing required parameter/field: ${object}`) @@ -25,11 +61,15 @@ function assertParamExists(object: string, value: string|boolean|number|undefine } } +/** + * Adds application details to the request body for a static code scan. + * + * @param {StaticCodeRequest} dataObj - The input data object containing application details. + * @returns {StaticCodeRequest} The request body with the application details added. + * @throws Will throw an error if required application details are missing. + */ function addApplicationToRequestBody(dataObj: StaticCodeRequest): StaticCodeRequest { - const bodyObj: ApplicationRequestBody = { - applicationName: '', - version: '', - } + const bodyObj: ApplicationRequestBody = {applicationName: '', version: ''} const requestBody: StaticCodeRequest = {} try { @@ -49,16 +89,33 @@ function addApplicationToRequestBody(dataObj: StaticCodeRequest): StaticCodeRequ return requestBody } +/** + * Adds application findings fields from the data object to the body object. + * + * @param bodyObject - The object to which the application findings will be added. + * @param dataObj - The object containing the application findings data. + * + * @throws Will throw an error if required fields are missing in the application findings. + * + * The function processes each finding in the `dataObj.applicationFindings` array. + * If a finding has the `clearFindings` property, it adds it directly to the `findingsArray`. + * Otherwise, it validates the presence of required fields (`codeCheckName`, `count`, + * `cweId`, `scanDate`) and optionally includes the `rawSeverity` field if present. + * + * The processed findings are then added to the `bodyObject.applicationFindings` array. + */ function addApplicationFindingsFields(bodyObject: StaticCodeRequest, dataObj: StaticCodeRequest): void { - const findingsArray: StaticCodeApplication[] = [] + const findingsArray: StaticCodeApplicationPost[] = [] try { - let findingsObj: StaticCodeApplication = {} + let findingsObj: StaticCodeApplicationPost = {} let i = 0 - dataObj.applicationFindings?.forEach((appFindings: StaticCodeApplication) => { + dataObj.applicationFindings?.forEach((appFindings: StaticCodeApplicationPost) => { + // If clearing findings if (Object.prototype.hasOwnProperty.call(appFindings, 'clearFindings')) { findingsObj.clearFindings = appFindings.clearFindings findingsArray.push(findingsObj) + // Adding findings } else { assertParamExists(`applicationFindings[${i}].codeCheckName`, appFindings.codeCheckName) assertParamExists(`applicationFindings[${i}].count`, appFindings.count) @@ -89,22 +146,33 @@ function addApplicationFindingsFields(bodyObject: StaticCodeRequest, dataObj: St bodyObject.applicationFindings = findingsArray } +const CMD_HELP = 'saf emasser post static_code_scans -h or --help' export default class EmasserPostStaticCodeScans extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' - static description = "upload application scan findings into a system's assets module" + static readonly description = "Upload application scan findings into a system's assets module" - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--cloudResourceFile]', - 'The input file should be a well formed JSON containing application scan findings.', + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--dataFile]', + 'The input file should be a well formed JSON containing static code scan findings.', + '\x1B[1m\x1B[46mAdd Findings\x1B[0m', 'Required "application" JSON object parameter/fields are: ', colorize(JSON.stringify(getJsonExamples('scan_findings-application'), null, 2)), 'Required "applicationFindings" JSON array parameters/fields are:', colorize(JSON.stringify(getJsonExamples('scan_findings-applicationFindings'), null, 2)), - 'Required "applicationFindings" JSON array for clearing findings for an application is:', - colorize(JSON.stringify(getJsonExamples('scan_findings-clearFindings'), null, 2))] - - static flags = { - help: Flags.help({char: 'h', description: 'Post (upload) static code scans, can also clear application\'s findings'}), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples('add')), + '\x1B[1m\x1B[46mClear Findings\x1B[0m \x1B[33m(can only be used on a single application with a single finding)\x1B[0m', + 'Required "application" JSON object parameter/fields are: ', + colorize(JSON.stringify(getJsonExamples('scan_findings-application'), null, 2)), + 'Required "applicationFindings" JSON array object field(s):', + colorize(JSON.stringify(getJsonExamples('scan_findings-clearFindings'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples('clear')), + ] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST Static Code Scans command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -116,19 +184,14 @@ export default class EmasserPostStaticCodeScans extends Command { const requestBodyArray: StaticCodeRequest[] = [] // Check if a Cloud Resource json file was provided - if (fs.existsSync(flags.statiCodeScanFile)) { + if (fs.existsSync(flags.dataFile)) { let data: any try { - data = JSON.parse(await readFile(flags.statiCodeScanFile, 'utf8')) + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) } catch (error: any) { - if (error.code === 'ENOENT') { - console.log('Scan Findings JSON file not found!') - process.exit(1) - } else { - console.log('Error reading Scan Findings file, possible malformed json. Please use the -h flag for help.') - console.log('Error message was:', error.message) - process.exit(1) - } + console.error('\x1B[91m» Error reading Static Code Scans data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) } // Create request body based on key/pair values provide in the input file @@ -157,12 +220,23 @@ export default class EmasserPostStaticCodeScans extends Command { } } } else { - console.error('Invalid or Scan Findings JSON file not found on the provided directory:', flags.statiCodeScanFile) + console.error('\x1B[91m» Static Code Scans data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') process.exit(1) } + // Call the API endpoint addStaticCodeScans.addStaticCodeScansBySystemId(flags.systemId, requestBodyArray).then((response: StaticCodeResponsePost) => { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/post/test_results.ts b/src/commands/emasser/post/test_results.ts index c9b6aeb89..3badd8d12 100644 --- a/src/commands/emasser/post/test_results.ts +++ b/src/commands/emasser/post/test_results.ts @@ -10,15 +10,17 @@ import {TestResultsApi} from '@mitre/emass_client' import {TestResultsResponsePost, TestResultsGet as TestResult} from '@mitre/emass_client/dist/api' +const CMD_HELP = 'saf emasser post test_results -h or --help' export default class EmasserPostTestResults extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]' - static description = "Add test results for a system's Assessment Procedures (CCIs) which determine Security Control compliance" + static readonly description = "Add test results for a system's Assessment Procedures which determine Security Control compliance\n" + + 'See the FLAGS section for required fields and acceptable values' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-c,--cci] [-b,--testedBy] [-t,--testDate] [-d,--description] [-S,--complianceStatus]'] + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-a,--assessmentProcedure] [-b,--testedBy] [-t,--testDate] [-d,--description] [-S,--complianceStatus]'] - static flags = { - help: Flags.help({char: 'h', description: 'Post (add) test results to a system\'s Assessment Procedures (CCIs)'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the POST Test Results command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -29,7 +31,7 @@ export default class EmasserPostTestResults extends Command { const requestBodyArray: TestResult[] = [] requestBodyArray.push({ - cci: flags.cci, + assessmentProcedure: flags.assessmentProcedure, testedBy: flags.testedBy, testDate: Number.parseFloat(flags.testDate), description: flags.description, @@ -38,6 +40,16 @@ export default class EmasserPostTestResults extends Command { addTestResults.addTestResultsBySystemId(flags.systemId, requestBodyArray).then((response: TestResultsResponsePost) => { console.log(colorize(outputFormat(response, false))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } } } diff --git a/src/commands/emasser/put/artifacts.ts b/src/commands/emasser/put/artifacts.ts index 7a2cb22e4..b68c17fe3 100644 --- a/src/commands/emasser/put/artifacts.ts +++ b/src/commands/emasser/put/artifacts.ts @@ -1,22 +1,121 @@ import {colorize} from 'json-colorizer' +import fs from 'fs' +import {readFile} from 'fs/promises' import {Command, Flags} from '@oclif/core' + import {ArtifactsApi} from '@mitre/emass_client' import {ArtifactsResponsePutPost} from '@mitre/emass_client/dist/api' // skipcq: JS-R1000 -import {ArtifactsGet as Artifacts} from '@mitre/emass_client/dist/api' // skipcq: JS-R1000 +import {ArtifactsResponseGetDataInner as Artifacts} from '@mitre/emass_client/dist/api' // skipcq: JS-R1000 + import {ApiConnection} from '../../../utils/emasser/apiConnection' import {outputFormat} from '../../../utils/emasser/outputFormatter' -import {FlagOptions, getFlagsForEndpoint} from '../../../utils/emasser/utilities' +import {FlagOptions, getFlagsForEndpoint, getJsonExamples, printRedMsg} from '../../../utils/emasser/utilities' import {outputError} from '../../../utils/emasser/outputError' +function getAllJsonExamples(): string { + let exampleBodyObj: any = {} + + exampleBodyObj = { + ...getJsonExamples('artifacts-put-required'), + ...getJsonExamples('artifacts-put-optional'), + } + + return exampleBodyObj +} + +function assertParamExists(object: string, value: string|number|boolean|undefined|null): void { + if (value === undefined) { + printRedMsg(`Missing required parameter/field: ${object}`) + throw new Error('Value not defined') + } +} + +function addRequiredFieldsToRequestBody(dataObj: Artifacts): Artifacts { + const bodyObj: Artifacts = {} + try { + assertParamExists('filename', dataObj.filename) + assertParamExists('isTemplate', dataObj.isTemplate) + assertParamExists('type', dataObj.type) + assertParamExists('category', dataObj.category) + } catch (error) { + console.log('Required JSON fields are:') + console.log(colorize(JSON.stringify(getJsonExamples('artifacts-put-required'), null, 2))) + throw error + } + + bodyObj.filename = dataObj.filename + bodyObj.isTemplate = dataObj.isTemplate + bodyObj.type = dataObj.type + bodyObj.category = dataObj.category + + return bodyObj +} + +function addOptionalFields(bodyObject: Artifacts, dataObj: Artifacts): void { + if (Object.prototype.hasOwnProperty.call(dataObj, 'name')) { + bodyObject.name = dataObj.name + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'description')) { + bodyObject.description = dataObj.description + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'referencePageNumber')) { + bodyObject.referencePageNumber = dataObj.referencePageNumber + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'controls')) { + bodyObject.controls = dataObj.controls + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'assessmentProcedures')) { + bodyObject.assessmentProcedures = dataObj.assessmentProcedures + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'expirationDate')) { + bodyObject.expirationDate = dataObj.expirationDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'lastReviewedDate')) { + bodyObject.lastReviewedDate = dataObj.lastReviewedDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'signedDate')) { + bodyObject.signedDate = dataObj.signedDate + } +} + +function generateBodyObj(dataObject: Artifacts): Artifacts { + let bodyObj: Artifacts = {} + try { + bodyObj = addRequiredFieldsToRequestBody(dataObject) + addOptionalFields(bodyObj, dataObject) + } catch { + process.exit(1) + } + + return bodyObj +} + +const CMD_HELP = 'saf emasser put artifacts -h or --help' export default class EmasserPutArtifacts extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' - static description = 'Updates artifacts for a system with provided entries' + static readonly description = 'Updates artifacts for a system with provided entries' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--filename] [--isTemplate,--no-isTemplate] [-t,--type] [-g--category] [options]'] + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--dataFile]', + 'The input file should be a well formed JSON containing the POA&M information based on defined business rules.', + 'Required JSON parameter/fields are: ', + colorize(JSON.stringify(getJsonExamples('artifacts-put-required'), null, 2)), + 'Optional JSON parameters/fields are:', + colorize(JSON.stringify(getJsonExamples('artifacts-put-optional'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples()), + ] - static flags = { - help: Flags.help({char: 'h', description: 'Put (update) one or many artifacts in a system'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the PUT Artifacts command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -26,22 +125,47 @@ export default class EmasserPutArtifacts extends Command { const artifactApi = new ArtifactsApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) const requestBodyArray: Artifacts[] = [] - requestBodyArray.push({ - filename: flags.filename, - isTemplate: flags.isTemplate, - type: flags.type, - category: flags.category, - // Optional arguments - description: flags.description, - referencePageNumber: flags.referencePageNumber, - ccis: flags.ccis, - controls: flags.controls, - expirationDate: Number.parseFloat(flags.expirationDate), - lastReviewedDate: Number.parseFloat(flags.lastReviewDate), - }) + // Check if a Artifacts json file was provided + if (fs.existsSync(flags.dataFile)) { + let data: any + try { + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) + } catch (error: any) { + console.error('\x1B[91m» Error reading Artifacts data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) + } + + // Artifacts json file provided, check if we have multiple Artifacts to process + if (Array.isArray(data)) { + data.forEach((dataObject: Artifacts) => { + // Generate the put request object based on business logic + requestBodyArray.push(generateBodyObj(dataObject)) + }) + } else if (typeof data === 'object') { + const dataObject: Artifacts = data + // Generate the put request object based on business logic + requestBodyArray.push(generateBodyObj(dataObject)) + } + } else { + console.error('\x1B[91m» Artifacts data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') + process.exit(1) + } + + // Call API endpoint artifactApi.updateArtifactBySystemId(flags.systemId, requestBodyArray).then((response: ArtifactsResponsePutPost) => { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/put/controls.ts b/src/commands/emasser/put/controls.ts index b87f68200..062aa344e 100644 --- a/src/commands/emasser/put/controls.ts +++ b/src/commands/emasser/put/controls.ts @@ -7,18 +7,56 @@ import {Command, Flags} from '@oclif/core' import {outputError} from '../../../utils/emasser/outputError' import {ApiConnection} from '../../../utils/emasser/apiConnection' import {outputFormat} from '../../../utils/emasser/outputFormatter' -import {FlagOptions, getFlagsForEndpoint, getJsonExamples} from '../../../utils/emasser/utilities' +import {FlagOptions, getFlagsForEndpoint, getJsonExamples, printHelpMsg, printRedMsg} from '../../../utils/emasser/utilities' import {ControlsApi} from '@mitre/emass_client' -import {ControlsResponsePut, - ControlsGet as Controls} from '@mitre/emass_client/dist/api' - -function printHelpMsg() { - console.log('\x1B[93m', '\nInvoke saf emasser put controls [-h, --help] for additional help', '\x1B[0m') +import {ControlsResponsePut} from '@mitre/emass_client/dist/api' + +interface Controls { + // Required Fields + acronym?: string + responsibleEntities?: string + controlDesignation?: string + estimatedCompletionDate?: string + implementationNarrative?: string + + // Conditional Fields + commonControlProvider?: string + naJustification?: string + slcmCriticality?: string + slcmFrequency?: string + slcmMethod?: string + slcmReporting?: string + slcmTracking?: string + slcmComments?: string + + // Optional Fields + implementationStatus?: string + severity?: string + vulnerabiltySummary?: string + recommendations?: string + relevanceOfThreat?: string + likelihood?: string + impact?: string + impactDescription?: string + residualRiskLevel?: string + testMethod?: string + mitigations?: string + applicationLayer?: string + databaseLayer?: string + operatingSystemLayer?: string } -function printRedMsg(msg: string) { - console.log('\x1B[91m', msg, '\x1B[0m') +function getAllJsonExamples(): string { + let exampleBodyObj: any = {} + + exampleBodyObj = { + ...getJsonExamples('controls-required'), + ...getJsonExamples('controls-conditional'), + ...getJsonExamples('controls-optional'), + } + + return exampleBodyObj } function assertParamExists(object: string, value: string|number|undefined|null): void { @@ -125,6 +163,22 @@ function addOptionalFields(bodyObject: Controls, dataObj: Controls): void { if (Object.prototype.hasOwnProperty.call(dataObj, 'testMethod')) { bodyObject.testMethod = dataObj.testMethod } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'mitigations')) { + bodyObject.mitigations = dataObj.mitigations + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'applicationLayer')) { + bodyObject.applicationLayer = dataObj.applicationLayer + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'databaseLayer')) { + bodyObject.databaseLayer = dataObj.databaseLayer + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'operatingSystemLayer')) { + bodyObject.operatingSystemLayer = dataObj.operatingSystemLayer + } } function processBusinessLogic(bodyObject: Controls, dataObj: Controls): void { // skipcq: JS-0044 @@ -142,8 +196,8 @@ function processBusinessLogic(bodyObject: Controls, dataObj: Controls): void { / // "Inherited" Only the following fields can be updated: // controlDesignation, commonnControlProvider //---------------------------------------------------------------------------------------- - - // Only process if we have an Implementation Status + const HELP_MSG = 'Invoke saf emasser put controls [-h, --help] for additional help' + // Only process if we have an Implementation Status (optional field) if (Object.prototype.hasOwnProperty.call(dataObj, 'implementationStatus')) { // The implementation Status is always required in any of these cases bodyObject.implementationStatus = dataObj.implementationStatus @@ -159,7 +213,7 @@ function processBusinessLogic(bodyObject: Controls, dataObj: Controls): void { / printRedMsg('Missing one of these parameters/fields:') printRedMsg(' responsibleEntities, slcmCriticality, slcmFrequency,') printRedMsg(' slcmMethod,slcmReporting, slcmTracking, slcmComments') - printHelpMsg() + printHelpMsg(HELP_MSG) process.exit(1) } else { bodyObject.responsibleEntities = dataObj.responsibleEntities @@ -182,7 +236,7 @@ function processBusinessLogic(bodyObject: Controls, dataObj: Controls): void { / } else { printRedMsg('Missing one of these parameters/fields:') printRedMsg(' naJustification, responsibleEntities') - printHelpMsg() + printHelpMsg(HELP_MSG) process.exit(1) } @@ -198,7 +252,7 @@ function processBusinessLogic(bodyObject: Controls, dataObj: Controls): void { / printRedMsg('Missing one of these parameters/fields:') printRedMsg(' commonControlProvider, responsibleEntities, slcmCriticality,') printRedMsg(' slcmFrequency, slcmMethod, slcmReporting, slcmTracking, slcmComments') - printHelpMsg() + printHelpMsg(HELP_MSG) process.exit(1) } else { bodyObject.commonControlProvider = dataObj.commonControlProvider @@ -220,7 +274,7 @@ function processBusinessLogic(bodyObject: Controls, dataObj: Controls): void { / bodyObject.commonControlProvider = dataObj.commonControlProvider } else { printRedMsg('When implementationStatus value is "Inherited" the following field is required: commonControlProvider') - printHelpMsg() + printHelpMsg(HELP_MSG) process.exit(1) } @@ -251,22 +305,27 @@ function generateBodyObj(dataObject: Controls): Controls { return bodyObj } +const CMD_HELP = 'saf emasser put controls -h or --help' export default class EmasserPutControls extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' - static description = 'Update Security Control information of a system for both the Implementation Plan and Risk Assessment.' + static readonly description = 'Update Security Control information of a system for both the Implementation Plan and Risk Assessment.' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--controlsFile]', + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f, --dataFile]', 'The input file should be a well formed JSON containing the Security Control information based on defined business rules.', 'Required JSON parameter/fields are: ', colorize(JSON.stringify(getJsonExamples('controls-required'), null, 2)), 'Conditional JSON parameters/fields are: ', colorize(JSON.stringify(getJsonExamples('controls-conditional'), null, 2)), 'Optional JSON parameters/fields are:', - colorize(JSON.stringify(getJsonExamples('controls-optional'), null, 2))] + colorize(JSON.stringify(getJsonExamples('controls-optional'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples()), + ] - static flags = { - help: Flags.help({char: 'h', description: 'Put (update) control information in a system for one or many controls. See emasser Features (emasserFeatures.md) for additional information.'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the PUT Controls command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -278,19 +337,14 @@ export default class EmasserPutControls extends Command { const requestBodyArray: Controls[] = [] // Check if a Security Control information json file was provided - if (fs.existsSync(flags.controlFile)) { + if (fs.existsSync(flags.dataFile)) { let data: any try { - data = JSON.parse(await readFile(flags.controlFile, 'utf8')) + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) } catch (error: any) { - if (error.code === 'ENOENT') { - console.log('Security Control information JSON file not found!') - process.exit(1) - } else { - console.log('Error reading Security Control information file, possible malformed json. Please use the -h flag for help.') - console.log('Error message was:', error.message) - process.exit(1) - } + console.error('\x1B[91m» Error reading Security Control(s) data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) } // Security Control information json file provided, check if we have multiple content to process @@ -305,7 +359,7 @@ export default class EmasserPutControls extends Command { requestBodyArray.push(generateBodyObj(dataObject)) } } else { - console.error('Invalid or Security Control information JSON file not found on the provided directory:', flags.controlFile) + console.error('\x1B[91m» The provided Security Control(s) data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') process.exit(1) } @@ -313,4 +367,14 @@ export default class EmasserPutControls extends Command { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/put/hardware_baseline.ts b/src/commands/emasser/put/hardware_baseline.ts new file mode 100644 index 000000000..6c05581f4 --- /dev/null +++ b/src/commands/emasser/put/hardware_baseline.ts @@ -0,0 +1,313 @@ +/* eslint-disable valid-jsdoc */ + +import fs from 'fs' +import _ from 'lodash' +import {readFile} from 'fs/promises' +import {colorize} from 'json-colorizer' +import {Command, Flags} from '@oclif/core' + +import { + FlagOptions, + getFlagsForEndpoint, + getJsonExamples, + printRedMsg, +} from '../../../utils/emasser/utilities' +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import {outputError} from '../../../utils/emasser/outputError' + +import {HardwareBaselineApi} from '@mitre/emass_client' +import {HwBaselineResponsePostPut as HwBaselineResponse} from '@mitre/emass_client/dist/api' + +/** + * Represents the hardware baseline configuration for an asset. + * + * @interface Hardware + * Required properties + * @property {string} [assetName] - The name of the asset. + * @property {string} [hardwareId] - The unique identifier for the hardware. + * Conditional properties + * @property {string} [publicFacingFqdn] - The fully qualified domain name if the asset is public-facing. + * @property {string} [publicFacingIpAddress] - The IP address if the asset is public-facing. + * @property {string} [publicFacingUrls] - The URLs if the asset is public-facing. + * Optional properties + * @property {string} [componentType] - The type of the component. + * @property {string} [nickname] - The nickname for the asset. + * @property {string} [assetIpAddress] - The IP address of the asset. + * @property {boolean} [publicFacing] - Indicates if the asset is public-facing. + * @property {boolean} [virtualAsset] - Indicates if the asset is virtual. + * @property {string} [manufacturer] - The manufacturer of the hardware. + * @property {string} [modelNumber] - The model number of the hardware. + * @property {string} [serialNumber] - The serial number of the hardware. + * @property {string} [osIosFwVersion] - The operating system, iOS, or firmware version of the hardware. + * @property {string} [memorySizeType] - The size and type of the memory. + * @property {string} [location] - The physical location of the hardware. + * @property {string} [approvalStatus] - The approval status of the hardware. + * @property {boolean} [criticalAsset] - Indicates if the asset is critical. + */ +interface Hardware { + // Required field + assetName?: string, + hardwareId?: string, + // Conditional Fields + publicFacingFqdn?: string, + publicFacingIpAddress?: string, + publicFacingUrls?: string, + // Optional Fields + componentType?: string, + nickname?: string, + assetIpAddress?: string, + publicFacing?: boolean, + virtualAsset?: boolean, + manufacturer?: string, + modelNumber?: string, + serialNumber?: string, + osIosFwVersion?: string, + memorySizeType?: string, + location?: string, + approvalStatus?: string, + criticalAsset?: boolean +} + +/** + * Combines JSON examples from multiple sources into a single object. + * + * This function aggregates JSON examples by merging the results of + * `getJsonExamples` calls for 'hardware-put-required', 'hardware-post-put-conditional', + * and 'hardware-post-put-optional' into a single object. + * + * @returns {string} A stringified JSON object containing the combined examples. + */ +function getAllJsonExamples(): string { + let exampleBodyObj: any = {} + + exampleBodyObj = { + ...getJsonExamples('hardware-put-required'), + ...getJsonExamples('hardware-post-put-conditional'), + ...getJsonExamples('hardware-post-put-optional'), + } + + return exampleBodyObj +} + +/** + * Asserts that a required parameter exists. + * + * @param object - The name of the parameter or field being checked. + * @param value - The value of the parameter or field. Can be a string, undefined, or null. + * @throws Will throw an error if the value is undefined. + */ +function assertParamExists(object: string, value: string|undefined|null): void { + if (value === undefined) { + printRedMsg(`Missing required parameter/field: ${object}`) + throw new Error('Value not defined') + } +} + +/** + * Adds required fields to the request body for a hardware object. + * + * This function ensures that the required fields `hardwareId` and `assetName` + * are present in the provided `dataObj`. If any of these fields are missing, + * an error is thrown and an example JSON structure is logged. + * + * @param dataObj - The hardware object containing the data to be validated and added to the request body. + * @returns A new hardware object containing only the required fields. + * @throws Will throw an error if `hardwareId` or `assetName` are missing from `dataObj`. + */ +function addRequiredFieldsToRequestBody(dataObj: Hardware): Hardware { + const bodyObj: Hardware = {} + + try { + assertParamExists('hardwareId', dataObj.hardwareId) + assertParamExists('assetName', dataObj.assetName) + } catch (error) { + console.log('Required JSON fields are:') + console.log(colorize(JSON.stringify(getJsonExamples('hardware-put-required'), null, 2))) + throw error + } + + // The required parameter "systemId" is validated by oclif + bodyObj.hardwareId = dataObj.hardwareId + bodyObj.assetName = dataObj.assetName + + return bodyObj +} + +/** + * Adds conditional fields from the `dataObj` to the `bodyObject` if they exist. + * + * @param bodyObject - The target object to which fields will be added. + * @param dataObj - The source object from which fields will be copied if they exist. + */ +function addConditionalFields(bodyObject: Hardware, dataObj: Hardware): void { + if (Object.prototype.hasOwnProperty.call(dataObj, 'publicFacingFqdn')) { + bodyObject.publicFacingFqdn = dataObj.publicFacingFqdn + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'publicFacingIpAddress')) { + bodyObject.publicFacingIpAddress = dataObj.publicFacingIpAddress + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'publicFacingUrls')) { + bodyObject.publicFacingUrls = dataObj.publicFacingUrls + } +} + +/** + * Adds optional fields from the data object to the body object if they exist. + * + * @param bodyObject - The target object to which optional fields will be added. + * @param dataObj - The source object containing optional fields. + */ +function addOptionalFields(bodyObject: Hardware, dataObj: Hardware): void { + if (Object.prototype.hasOwnProperty.call(dataObj, 'componentType')) { + bodyObject.componentType = dataObj.componentType + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nickname')) { + bodyObject.nickname = dataObj.nickname + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'assetIpAddress')) { + bodyObject.assetIpAddress = dataObj.assetIpAddress + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'publicFacing')) { + bodyObject.publicFacing = dataObj.publicFacing + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'virtualAsset')) { + bodyObject.virtualAsset = dataObj.virtualAsset + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'manufacturer')) { + bodyObject.manufacturer = dataObj.manufacturer + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'modelNumber')) { + bodyObject.modelNumber = dataObj.modelNumber + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'serialNumber')) { + bodyObject.serialNumber = dataObj.serialNumber + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'osIosFwVersion')) { + bodyObject.osIosFwVersion = dataObj.osIosFwVersion + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'memorySizeType')) { + bodyObject.memorySizeType = dataObj.memorySizeType + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'location')) { + bodyObject.location = dataObj.location + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'approvalStatus')) { + bodyObject.approvalStatus = dataObj.approvalStatus + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'criticalAsset')) { + bodyObject.criticalAsset = dataObj.criticalAsset + } +} + +/** + * Generates a body object for a hardware request by adding required, conditional, and optional fields. + * + * @param dataObject - The hardware data object to be processed. + * @returns The generated body object with the necessary fields. + * @throws Will exit the process with code 1 if an error occurs during the generation. + */ +function generateBodyObj(dataObject: Hardware): Hardware { + let bodyObj: Hardware = {} + + try { + bodyObj = addRequiredFieldsToRequestBody(dataObject) + addConditionalFields(bodyObj, dataObject) + addOptionalFields(bodyObj, dataObject) + } catch { + process.exit(1) + } + + return bodyObj +} + +const CMD_HELP = 'saf emasser post hardware_baseline -h or --help' +export default class EmasserHardwareBaseline extends Command { + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' + + static readonly description = 'Update one or many hardware assets to a system.\n' + + 'The CLI expects an input JSON file containing the required, conditional\n' + + 'and optional fields for the hardware asset(s) being added to the system.' + + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--dataFile]', + 'The input file should be a well formed JSON containing Hardware Assets.', + '\x1B[1mRequired JSON parameter/field is:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('hardware-put-required'), null, 2)), + '\x1B[1mConditional JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('hardware-post-put-conditional'), null, 2)), + '\x1B[1mOptional JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('hardware-post-put-optional'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples()), + ] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the PUT Hardware Baseline command'}), + ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 + } + + async run(): Promise { + const {flags} = await this.parse(EmasserHardwareBaseline) + const apiCxn = new ApiConnection() + const hwBaseline = new HardwareBaselineApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + const requestBodyArray: Hardware[] = [] + + // Check if a Hardware json file was provided + if (fs.existsSync(flags.dataFile)) { + let data: any + try { + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) + } catch (error: any) { + console.error('\x1B[91m» Error reading Hardware data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) + } + + // Process the Hardware data file + if (Array.isArray(data)) { + data.forEach((dataObject: Hardware) => { + // Generate the put request object + requestBodyArray.push(generateBodyObj(dataObject)) + }) + } else if (typeof data === 'object') { + const dataObject: Hardware = data + // Generate the put request object + requestBodyArray.push(generateBodyObj(dataObject)) + } + } else { + console.error('\x1B[91m» Hardware data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') + process.exit(1) + } + + // Call the endpoint + hwBaseline.updateHwBaselineAssets(flags.systemId, requestBodyArray).then((response: HwBaselineResponse) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error: any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } +} diff --git a/src/commands/emasser/put/milestones.ts b/src/commands/emasser/put/milestones.ts index 26aed63d8..5f850e3b3 100644 --- a/src/commands/emasser/put/milestones.ts +++ b/src/commands/emasser/put/milestones.ts @@ -10,15 +10,16 @@ import {MilestonesApi} from '@mitre/emass_client' import {MilestoneResponsePut, MilestonesGet as Milestones} from '@mitre/emass_client/dist/api' +const CMD_HELP = 'saf emasser put milestones -h or --help' export default class EmasserPutMilestones extends Command { - static usage = '<%= command.id %> [options]'; + static readonly usage = '<%= command.id %> [options]'; - static description = 'Update milestone(s) for specified system, poam, and milestone Id'; + static readonly description = 'Update milestone(s) for specified system, poam, and milestone Id'; - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-p,--poamId] [-m,--milestoneId] [-d,--description] [-c,--scheduledCompletionDate]']; + static readonly examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-p,--poamId] [-m,--milestoneId] [-d,--description] [-c,--scheduledCompletionDate]']; - static flags = { - help: Flags.help({char: 'h', description: 'Show emasser CLI help for the PUT Milestones endpoint'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the PUT Milestones command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -34,8 +35,19 @@ export default class EmasserPutMilestones extends Command { scheduledCompletionDate: Number.parseFloat(flags.scheduledCompletionDate), }) + // Call API endpoint putMilestones.updateMilestoneBySystemIdAndPoamId(flags.systemId, flags.poamId, requestBodyArray).then((response: MilestoneResponsePut) => { console.log(colorize(outputFormat(response, false))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/put/poams.ts b/src/commands/emasser/put/poams.ts index 81461ea85..ae7a580a4 100644 --- a/src/commands/emasser/put/poams.ts +++ b/src/commands/emasser/put/poams.ts @@ -1,3 +1,4 @@ +/* eslint-disable valid-jsdoc */ import fs from 'fs' import _ from 'lodash' import {readFile} from 'fs/promises' @@ -7,20 +8,149 @@ import {Command, Flags} from '@oclif/core' import {outputError} from '../../../utils/emasser/outputError' import {ApiConnection} from '../../../utils/emasser/apiConnection' import {outputFormat} from '../../../utils/emasser/outputFormatter' -import {FlagOptions, getFlagsForEndpoint, getJsonExamples} from '../../../utils/emasser/utilities' +import {FlagOptions, getFlagsForEndpoint, getJsonExamples, printHelpMsg, printRedMsg} from '../../../utils/emasser/utilities' import {POAMApi} from '@mitre/emass_client' -import {MilestonesGet, PoamResponsePut, - PoamGet as Poams} from '@mitre/emass_client/dist/api' - -function printRedMsg(msg: string) { - console.log('\x1B[91m', msg, '\x1B[0m') -} +// import {MilestonesGet, PoamResponsePut, +// PoamGet as Poams} from '@mitre/emass_client/dist/api' +import {MilestonesRequiredPutMilestonesInner as MilestonesRequiredPut, PoamResponsePostPutDelete} from '@mitre/emass_client/dist/api' + +/** + * Interface representing a Plan of Action and Milestones (POAMs) object. + * + * @property {number} [poamId] - The unique identifier for the POAM. + * @property {string} [displayPoamId] - The display identifier for the POAM. + * @property {string} [status] - The current status of the POAM. + * @property {string} [vulnerabilityDescription] - Description of the vulnerability addressed by the POAM. + * @property {string} [sourceIdentifyingVulnerability] - Source identifying the vulnerability. + * @property {string} [pocOrganization] - Point of contact organization. + * @property {string} [resources] - Resources associated with the POAM. + * + * @property {Array} [milestones] - List of milestones associated with the POAM. + * @property {string} [pocFirstName] - Point of contact first name. + * @property {string} [pocLastName] - Point of contact last name. + * @property {string} [pocEmail] - Point of contact email address. + * @property {string} [pocPhoneNumber] - Point of contact phone number. + * @property {string} [scheduledCompletionDate] - Scheduled completion date of the POAM. + * @property {string} [completionDate] - Actual completion date of the POAM. + * @property {string} [comments] - Additional comments regarding the POAM. + * @property {string} [severity] - Severity of the risk analysis, conditionally required in certain eMASS instances. + * + * @property {string} [externalUid] - External unique identifier. + * @property {string} [controlAcronym] - Acronym for the control. + * @property {string} [assessmentProcedure] - Assessment procedure details. + * @property {string} [securityChecks] - Security checks performed. + * @property {string} [rawSeverity] - Raw severity of the vulnerability. + * @property {string} [impactDescription] - Description of the impact. + * @property {string} [recommendations] - Recommendations for mitigation. + * @property {string} [resultingResidualRiskLevelAfterProposedMitigations] - Residual risk level after proposed mitigations. + * @property {string} [predisposingConditions] - Predisposing conditions affecting the POAM. + * @property {string} [threatDescription] - Description of the threat. + * @property {string} [devicesAffected] - Devices affected by the vulnerability. + * @property {string} [mitigations] - Mitigations applied, conditionally required in certain eMASS instances. + * @property {string} [relevanceOfThreat] - Relevance of the threat. + * @property {string} [likelihood] - Likelihood of the threat occurring. + * @property {string} [impact] - Impact of the threat. + * @property {string} [residualRiskLevel] - Residual risk level after mitigations. + * @property {boolean} [identifiedInCFOAuditOrOtherReview] - Indicates if identified in CFO audit or other review, conditionally required for VA. + * @property {number} [personnelResourcesFundedBaseHours] - Funded base hours for personnel resources. + * @property {string} [personnelResourcesCostCode] - Cost code for personnel resources. + * @property {number} [personnelResourcesUnfundedBaseHours] - Unfunded base hours for personnel resources. + * @property {string} [personnelResourcesNonfundingObstacle] - Non-funding obstacle for personnel resources. + * @property {string} [personnelResourcesNonfundingObstacleOtherReason] - Other reason for non-funding obstacle for personnel resources. + * @property {number} [nonPersonnelResourcesFundedAmount] - Funded amount for non-personnel resources. + * @property {string} [nonPersonnelResourcesCostCode] - Cost code for non-personnel resources. + * @property {number} [nonPersonnelResourcesUnfundedAmount] - Unfunded amount for non-personnel resources. + * @property {string} [nonPersonnelResourcesNonfundingObstacle] - Non-funding obstacle for non-personnel resources. + * @property {string} [nonPersonnelResourcesNonfundingObstacleOtherReason] - Other reason for non-funding obstacle for non-personnel resources. + */ +interface Poams { + // Required Fields - Declared as undefined but validated later + poamId?: number + displayPoamId?: string + status?: string + vulnerabilityDescription?: string + sourceIdentifyingVulnerability?: string + pocOrganization?: string + resources?: string + + // Conditional Fields + milestones?: Array + pocFirstName?: string + pocLastName?: string + pocEmail?: string + pocPhoneNumber?: string + scheduledCompletionDate?: string + completionDate?: string + comments?: string + // Conditional but certain eMASS instances may + // require the severity Risk Analysis field + severity?: string + + // Optional + externalUid?: string + controlAcronym?: string + assessmentProcedure?: string + securityChecks?: string + rawSeverity?: string + impactDescription?: string + recommendations?: string + resultingResidualRiskLevelAfterProposedMitigations?: string + predisposingConditions?: string + threatDescription?: string + devicesAffected?: string + // Optional but certain eMASS instances + // may require these Risk Analysis fields + mitigations?: string + relevanceOfThreat?: string + likelihood?: string + impact?: string + residualRiskLevel?: string + // Optional for Army and USCG - Required (Conditional) for VA + identifiedInCFOAuditOrOtherReview?: boolean + personnelResourcesFundedBaseHours?: number + personnelResourcesCostCode?: string + personnelResourcesUnfundedBaseHours?: number + personnelResourcesNonfundingObstacle?: string + personnelResourcesNonfundingObstacleOtherReason?: string + nonPersonnelResourcesFundedAmount?: number + nonPersonnelResourcesCostCode?: string + nonPersonnelResourcesUnfundedAmount?: number + nonPersonnelResourcesNonfundingObstacle?: string + nonPersonnelResourcesNonfundingObstacleOtherReason?: string + } + +/** + * Retrieves a combined JSON example object by merging multiple JSON examples. + * + * The function combines JSON examples from the following sources: + * - 'poams-put-required' + * - 'poams-post-put-required-va' + * - 'poams-put-conditional' + * - 'poams-post-put-optional' + * + * @returns {string} A combined JSON example object as a string. + */ +function getAllJsonExamples(): string { + let exampleBodyObj: any = {} + + exampleBodyObj = { + ...getJsonExamples('poams-put-required'), + ...getJsonExamples('poams-post-put-required-va'), + ...getJsonExamples('poams-put-conditional'), + ...getJsonExamples('poams-post-put-optional'), + } -function printHelpMsg(msg: string) { - console.log('\x1B[93m', msg, '\x1B[0m') + return exampleBodyObj } +/** + * Asserts that a required parameter exists and is not undefined. + * + * @param object - The name of the parameter or field being checked. + * @param value - The value of the parameter or field to check. Can be a string, number, undefined, or null. + * @throws {Error} Throws an error if the value is undefined. + */ function assertParamExists(object: string, value: string|number|undefined|null): void { if (value === undefined) { printRedMsg(`Missing required parameter/field: ${object}`) @@ -28,6 +158,17 @@ function assertParamExists(object: string, value: string|number|undefined|null): } } +/** + * Adds required fields to the request body for a POAMs (Plan of Action and Milestones) object. + * + * This function ensures that all required fields are present in the input `dataObj` and then + * constructs a new `Poams` object with these fields. If any required field is missing, an error + * is thrown, and an example of the required JSON structure is logged. + * + * @param {Poams} dataObj - The input POAMs object containing the data to be validated and added to the request body. + * @returns {Poams} - A new POAMs object containing the required fields from the input `dataObj`. + * @throws Will throw an error if any required field is missing in the input `dataObj`. + */ function addRequiredFieldsToRequestBody(dataObj: Poams): Poams { const bodyObj: Poams = {} try { @@ -35,10 +176,9 @@ function addRequiredFieldsToRequestBody(dataObj: Poams): Poams { assertParamExists('displayPoamId', dataObj.displayPoamId) assertParamExists('status', dataObj.status) assertParamExists('vulnerabilityDescription', dataObj.vulnerabilityDescription) - assertParamExists('sourceIdentVuln', dataObj.sourceIdentVuln) + assertParamExists('sourceIdentifyingVulnerability', dataObj.sourceIdentifyingVulnerability) assertParamExists('pocOrganization', dataObj.pocOrganization) assertParamExists('resources', dataObj.resources) - assertParamExists('mitigation', dataObj.mitigation) } catch (error) { console.log('Required JSON fields are:') console.log(colorize(JSON.stringify(getJsonExamples('poams-put-required'), null, 2))) @@ -49,14 +189,24 @@ function addRequiredFieldsToRequestBody(dataObj: Poams): Poams { bodyObj.displayPoamId = dataObj.displayPoamId bodyObj.status = dataObj.status bodyObj.vulnerabilityDescription = dataObj.vulnerabilityDescription - bodyObj.sourceIdentVuln = dataObj.sourceIdentVuln + bodyObj.sourceIdentifyingVulnerability = dataObj.sourceIdentifyingVulnerability bodyObj.pocOrganization = dataObj.pocOrganization bodyObj.resources = dataObj.resources - bodyObj.mitigation = dataObj.mitigation return bodyObj } +/** + * Adds conditional fields from the data object to the body object if they exist. + * + * @param bodyObject - The target object to which fields will be added. + * @param dataObj - The source object from which fields will be copied. + * + * @remarks + * This function checks if the specified properties ('pocFirstName', 'pocLastName', + * 'pocEmail', 'pocPhoneNumber', 'severity') exist in the data object. If they do, + * it copies their values to the corresponding properties in the body object. + */ function addConditionalFields(bodyObject: Poams, dataObj: Poams): void { if (Object.prototype.hasOwnProperty.call(dataObj, 'pocFirstName')) { bodyObject.pocFirstName = dataObj.pocFirstName @@ -79,6 +229,13 @@ function addConditionalFields(bodyObject: Poams, dataObj: Poams): void { } } +/** + * Adds optional fields from the data object to the body object if they exist. + * + * @param bodyObject - The target object to which optional fields will be added. + * @param dataObj - The source object containing optional fields. + */ +// skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold function addOptionalFields(bodyObject: Poams, dataObj: Poams): void { if (Object.prototype.hasOwnProperty.call(dataObj, 'externalUid')) { bodyObject.externalUid = dataObj.externalUid @@ -88,8 +245,8 @@ function addOptionalFields(bodyObject: Poams, dataObj: Poams): void { bodyObject.controlAcronym = dataObj.controlAcronym } - if (Object.prototype.hasOwnProperty.call(dataObj, 'cci')) { - bodyObject.cci = dataObj.cci + if (Object.prototype.hasOwnProperty.call(dataObj, 'assessmentProcedure')) { + bodyObject.assessmentProcedure = dataObj.assessmentProcedure } if (Object.prototype.hasOwnProperty.call(dataObj, 'securityChecks')) { @@ -123,28 +280,121 @@ function addOptionalFields(bodyObject: Poams, dataObj: Poams): void { if (Object.prototype.hasOwnProperty.call(dataObj, 'recommendations')) { bodyObject.recommendations = dataObj.recommendations } - // if (Object.prototype.hasOwnProperty.call(dataObj,'mitigation')) {bodyObject.mitigation = dataObj.mitigation; } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'mitigations')) { + bodyObject.mitigations = dataObj.mitigations + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'resultingResidualRiskLevelAfterProposedMitigations')) { + bodyObject.resultingResidualRiskLevelAfterProposedMitigations = dataObj.resultingResidualRiskLevelAfterProposedMitigations + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'predisposingConditions')) { + bodyObject.predisposingConditions = dataObj.predisposingConditions + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'threatDescription')) { + bodyObject.threatDescription = dataObj.threatDescription + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'devicesAffected')) { + bodyObject.devicesAffected = dataObj.devicesAffected + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'identifiedInCFOAuditOrOtherReview')) { + bodyObject.identifiedInCFOAuditOrOtherReview = dataObj.identifiedInCFOAuditOrOtherReview + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'personnelResourcesFundedBaseHours')) { + bodyObject.personnelResourcesFundedBaseHours = dataObj.personnelResourcesFundedBaseHours + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'personnelResourcesCostCode')) { + bodyObject.personnelResourcesCostCode = dataObj.personnelResourcesCostCode + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'personnelResourcesUnfundedBaseHours')) { + bodyObject.personnelResourcesUnfundedBaseHours = dataObj.personnelResourcesUnfundedBaseHours + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'personnelResourcesNonfundingObstacle')) { + bodyObject.personnelResourcesNonfundingObstacle = dataObj.personnelResourcesNonfundingObstacle + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'personnelResourcesNonfundingObstacleOtherReason')) { + bodyObject.personnelResourcesNonfundingObstacleOtherReason = dataObj.personnelResourcesNonfundingObstacleOtherReason + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nonPersonnelResourcesFundedAmount')) { + bodyObject.nonPersonnelResourcesFundedAmount = dataObj.nonPersonnelResourcesFundedAmount + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nonPersonnelResourcesCostCode')) { + bodyObject.nonPersonnelResourcesCostCode = dataObj.nonPersonnelResourcesCostCode + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nonPersonnelResourcesUnfundedAmount')) { + bodyObject.nonPersonnelResourcesUnfundedAmount = dataObj.nonPersonnelResourcesUnfundedAmount + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nonPersonnelResourcesNonfundingObstacle')) { + bodyObject.nonPersonnelResourcesNonfundingObstacle = dataObj.nonPersonnelResourcesNonfundingObstacle + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'nonPersonnelResourcesNonfundingObstacleOtherReason')) { + bodyObject.nonPersonnelResourcesNonfundingObstacleOtherReason = dataObj.nonPersonnelResourcesNonfundingObstacleOtherReason + } } +/** + * Processes business logic for POA&M items based on their status and validates required fields. + * + * @param bodyObject - The object to populate with validated data. + * @param dataObj - The object containing input data to validate and process. + * + * The function performs the following checks based on the `status` field in `dataObj`: + * + * - "Risk Accepted": + * - Requires `comments`. + * - Cannot have `scheduledCompletionDate` or `milestones`. + * + * - "Ongoing": + * - Requires at least one `milestones` object. + * - Each `milestone` must have `description` and `scheduledCompletionDate`. + * - Optionally includes `scheduledCompletionDate`. + * + * - "Completed": + * - Requires `comments`, `completionDate`, and at least one `milestones` object. + * - Optionally includes `scheduledCompletionDate`. + * + * - "Not Applicable": + * - Cannot be created. + * + * - "Archived": + * - Cannot be updated. + * + * Additionally, if any POC (Point of Contact) fields are provided, all POC fields are required: + * - `pocFirstName` + * - `pocLastName` + * - `pocEmail` + * - `pocPhoneNumber` + * + * The function uses `printRedMsg` to display error messages and `printHelpMsg` to display help messages. + * It exits the process with a status code of 1 if any validation fails. +*/ +// skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold function processBusinessLogic(bodyObject: Poams, dataObj: Poams): void { // skipcq: JS-0044 - //----------------------------------------------------------------------------- - // Conditional fields that are required based on the "status" field value - // "Risk Accepted" comments, resources - // "Ongoing" scheduledCompletionDate, resources, milestones (at least 1) - // "Completed" scheduledCompletionDate, comments, resources, - // completionDate, milestones (at least 1) - // "Not Applicable" POAM can not be created - //----------------------------------------------------------------------------- - const HELP_MSG = '\nInvoke saf emasser post poams [-h, --help] for additional help' + const HELP_MSG = 'Invoke saf emasser post poams [-h, --help] for additional help' switch (dataObj.status) { case 'Risk Accepted': { if (dataObj.comments === undefined) { - printRedMsg('When status is "Risk Accepted" the following parameters/fields are required:') + printRedMsg('When status is "Risk Accepted" the following parameter/field is required:') printRedMsg(' comments') printHelpMsg(HELP_MSG) process.exit(1) + // Risk Accepted POA&M Item cannot be saved with a Scheduled Completion Date or Milestones. } else if (Object.prototype.hasOwnProperty.call(dataObj, 'scheduledCompletionDate') || Object.prototype.hasOwnProperty.call(dataObj, 'milestones')) { - printRedMsg('When status is "Risk Accepted" POA&Ms CAN NOT be saved with the following parameters/field:') + printRedMsg('When status is "Risk Accepted" POA&Ms CAN NOT be saved with the following parameters/fields:') printRedMsg(' scheduledCompletionDate, or milestone') printHelpMsg(HELP_MSG) process.exit(1) @@ -156,32 +406,37 @@ function processBusinessLogic(bodyObject: Poams, dataObj: Poams): void { // skip } case 'Ongoing': { - if (!(Object.prototype.hasOwnProperty.call(dataObj, 'scheduledCompletionDate') && Object.prototype.hasOwnProperty.call(dataObj, 'milestones'))) { - printRedMsg('When status is "Ongoing" the following parameters/fields are required:') - printRedMsg(' scheduledCompletionDate, milestones') + // Can not update scheduledCompletionDate if review status (reviewStatus is a readonly field) is Approved + // API call will return and error if scheduledCompletionDate for Approved review status POA&Ms + + // POA&M Items that have a status of “Ongoing” cannot be saved without Milestones. + if (!(Object.prototype.hasOwnProperty.call(dataObj, 'milestones'))) { + printRedMsg('When status is "Ongoing" and updating a POA&M a Milestone is required') printHelpMsg(HELP_MSG) process.exit(1) - } else if (((_.some(dataObj.milestones, function (milestone) { // skipcq: JS-0241 - return milestone.description - }))) || - ((_.some(dataObj.milestones, function (milestone) { // skipcq: JS-0241 - return milestone.scheduledCompletionDate - })))) { - printRedMsg('When milestones are define the parameters "description" and "scheduledCompletionDate" are required.') - printRedMsg('Missing one of the required milestones parameters:') - printRedMsg(' description, scheduledCompletionDate') + // If we have a milestone, ensure the required fields are provided. + } else if (!(_.some(dataObj.milestones, 'description')) || !(_.some(dataObj.milestones, 'scheduledCompletionDate'))) { + printRedMsg('Milestone object requires the following fields:') + printRedMsg(' "milestones": [{"description": "The milestone description", "scheduledCompletionDate": Unix date format }], ') process.exit(1) } else { - // Add the POA&M completion date - bodyObject.scheduledCompletionDate = dataObj.scheduledCompletionDate + // Add the POA&M schedule completion date if provided (backend may return an error) + if (Object.prototype.hasOwnProperty.call(dataObj, 'scheduledCompletionDate')) { + bodyObject.scheduledCompletionDate = dataObj.scheduledCompletionDate + } // Add the milestone object - const milestoneArray: Array = [] - dataObj.milestones?.forEach((milestone: MilestonesGet) => { - const milestoneObj: MilestonesGet = {} - milestoneObj.milestoneId = milestone.milestoneId + const milestoneArray: Array = [] + dataObj.milestones?.forEach((milestone: MilestonesRequiredPut) => { + const milestoneObj: MilestonesRequiredPut = {description: '', scheduledCompletionDate: 0, isActive: false} milestoneObj.description = milestone.description milestoneObj.scheduledCompletionDate = milestone.scheduledCompletionDate + // isActive is used to prevent uploading duplicate/undesired milestones via the + // POA&M PUT call and must be set to false, otherwise a new milestone is created + if (Object.prototype.hasOwnProperty.call(milestone, 'isActive')) { + milestoneObj.isActive = milestone.isActive + } + milestoneArray.push(milestoneObj) }) bodyObject.milestones = [...milestoneArray] @@ -191,25 +446,35 @@ function processBusinessLogic(bodyObject: Poams, dataObj: Poams): void { // skip } case 'Completed': { - if (!(Object.prototype.hasOwnProperty.call(dataObj, 'scheduledCompletionDate')) || !(Object.prototype.hasOwnProperty.call(dataObj, 'comments')) || - !(Object.prototype.hasOwnProperty.call(dataObj, 'completionDate')) || !(Object.prototype.hasOwnProperty.call(dataObj, 'milestones'))) { + // Completed POA&M Item require the completionDate, comments, and Milestones. + if (!(Object.prototype.hasOwnProperty.call(dataObj, 'comments')) || + !(Object.prototype.hasOwnProperty.call(dataObj, 'completionDate')) || + !(Object.prototype.hasOwnProperty.call(dataObj, 'milestones'))) { printRedMsg('When status is "Completed" the following parameters/fields are required:') - printRedMsg(' scheduledCompletionDate, comments, completionDate, or milestone') + printRedMsg(' comments, completionDate, and milestones') printHelpMsg(HELP_MSG) process.exit(1) } else { // Add the POA&M schedule and completion date, comments bodyObject.comments = dataObj.comments bodyObject.completionDate = dataObj.completionDate - bodyObject.scheduledCompletionDate = dataObj.scheduledCompletionDate + // Add the POA&M schedule completion date if provided (backend may return an error) + if (Object.prototype.hasOwnProperty.call(dataObj, 'scheduledCompletionDate')) { + bodyObject.scheduledCompletionDate = dataObj.scheduledCompletionDate + } // Add the milestone object - const milestoneArray: Array = [] - dataObj.milestones?.forEach((milestone: MilestonesGet) => { - const milestoneObj: MilestonesGet = {} - milestoneObj.milestoneId = milestone.milestoneId + const milestoneArray: Array = [] + dataObj.milestones?.forEach((milestone: MilestonesRequiredPut) => { + const milestoneObj: MilestonesRequiredPut = {description: '', scheduledCompletionDate: 0, isActive: false} milestoneObj.description = milestone.description milestoneObj.scheduledCompletionDate = milestone.scheduledCompletionDate + // isActive is used to prevent uploading duplicate/undesired milestones via the + // POA&M PUT call and must be set to false, otherwise a new milestone is created + if (Object.prototype.hasOwnProperty.call(milestone, 'isActive')) { + milestoneObj.isActive = milestone.isActive + } + milestoneArray.push(milestoneObj) }) bodyObject.milestones = [...milestoneArray] @@ -218,6 +483,12 @@ function processBusinessLogic(bodyObject: Poams, dataObj: Poams): void { // skip break } + case 'Not Applicable': { + printRedMsg('POA&M Items with a status of "Not Applicable" will be updated through test result creation.') + process.exit(0) + break + } + case 'Archived': { printHelpMsg('Archived POA&M Items cannot be updated') process.exit(0) @@ -234,16 +505,34 @@ function processBusinessLogic(bodyObject: Poams, dataObj: Poams): void { // skip } // POC checks: If any poc information is provided all POC fields are required - if ((Object.prototype.hasOwnProperty.call(dataObj, 'pocFirstName') || Object.prototype.hasOwnProperty.call(dataObj, 'pocLastName') || - Object.prototype.hasOwnProperty.call(dataObj, 'pocEmail') || Object.prototype.hasOwnProperty.call(dataObj, 'pocPhoneNumber')) && (!(Object.prototype.hasOwnProperty.call(dataObj, 'pocFirstName')) || !(Object.prototype.hasOwnProperty.call(dataObj, 'pocLastName')) || - !(Object.prototype.hasOwnProperty.call(dataObj, 'pocEmail')) || !(Object.prototype.hasOwnProperty.call(dataObj, 'pocPhoneNumber')))) { - printRedMsg('If any POC content is provided (pocFirstName, pocLastName, pocEmail, pocPhoneNumber) than all POC parameters are required:') - printRedMsg(' pocFirstName, pocLastName, pocEmail, pocPhoneNumber') + let missingFields = '' + if ((_.get(dataObj, 'pocFirstName') === undefined)) missingFields = 'pocFirstName' + if ((_.get(dataObj, 'pocLastName') === undefined)) missingFields += (missingFields === '') ? 'pocLastName' : ', pocLastName' + if ((_.get(dataObj, 'pocEmail') === undefined)) missingFields += (missingFields === '') ? 'pocEmail' : ', pocEmail' + if ((_.get(dataObj, 'pocPhoneNumber') === undefined)) missingFields += (missingFields === '') ? 'pocPhoneNumber' : ', pocPhoneNumber' + const totalPocMissingFields = missingFields.split(',').length + if ((totalPocMissingFields >= 1 && totalPocMissingFields < 4) && missingFields !== '') { + printRedMsg('If any POC fields are provided (pocFirstName, pocLastName, pocEmail, pocPhoneNumber) than all POC fields are required:') + printRedMsg(` Missing field(s): ${missingFields}`) printHelpMsg(HELP_MSG) process.exit(1) } } +/** + * Generates a new Poams object by processing the given dataObject. + * + * This function performs the following steps: + * 1. Adds required fields to the request body. + * 2. Processes business logic on the body object. + * 3. Adds conditional fields based on the dataObject. + * 4. Adds optional fields to the body object. + * + * If any error occurs during these steps, the process exits with code 1. + * + * @param dataObject - The input Poams object to be processed. + * @returns The newly generated Poams object with the required, conditional, and optional fields. + */ function generateBodyObj(dataObject: Poams): Poams { let bodyObj: Poams = {} try { @@ -258,22 +547,29 @@ function generateBodyObj(dataObject: Poams): Poams { return bodyObj } +const CMD_HELP = 'saf emasser put poams -h or --help' export default class EmasserPutPoams extends Command { - static usage = '<%= command.id %> [options]' + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' - static description = 'Update a Plan of Action and Milestones (POA&M) into a systems.' + static readonly description = 'Update a Plan of Action and Milestones (POA&M) into a systems.' - static examples = ['<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--poamFile]', + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--dataFile]', 'The input file should be a well formed JSON containing the POA&M information based on defined business rules.', 'Required JSON parameter/fields are: ', colorize(JSON.stringify(getJsonExamples('poams-put-required'), null, 2)), + '\x1B[1mRequired for VA but Conditional for Army and USCG JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('poams-post-put-required-va'), null, 2)), 'Conditional JSON parameters/fields are: ', colorize(JSON.stringify(getJsonExamples('poams-put-conditional'), null, 2)), 'Optional JSON parameters/fields are:', - colorize(JSON.stringify(getJsonExamples('poams-post-put-optional'), null, 2))] + colorize(JSON.stringify(getJsonExamples('poams-post-put-optional'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples()), + ] - static flags = { - help: Flags.help({char: 'h', description: 'Put (update) a Plan of Action and Milestones (POA&M) item(s) in a system. See emasser Features (emasserFeatures.md) for additional information.'}), + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the PUT POA&Ms command'}), ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 } @@ -285,19 +581,14 @@ export default class EmasserPutPoams extends Command { const requestBodyArray: Poams[] = [] // Check if a POA&Ms json file was provided - if (fs.existsSync(flags.poamFile)) { + if (fs.existsSync(flags.dataFile)) { let data: any try { - data = JSON.parse(await readFile(flags.poamFile, 'utf8')) + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) } catch (error: any) { - if (error.code === 'ENOENT') { - console.log('POA&Ms JSON file not found!') - process.exit(1) - } else { - console.log('Error reading POA&Ms file, possible malformed json. Please use the -h flag for help.') - console.log('Error message was:', error.message) - process.exit(1) - } + console.error('\x1B[91m» Error reading POA&Ms data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) } // POA&Ms json file provided, check if we have multiple POA&Ms to process @@ -312,12 +603,23 @@ export default class EmasserPutPoams extends Command { requestBodyArray.push(generateBodyObj(dataObject)) } } else { - console.error('Invalid or POA&M JSON file not found on the provided directory:', flags.poamFile) + console.error('\x1B[91m» POA&M(s) data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') process.exit(1) } - updatePoam.updatePoamBySystemId(flags.systemId, requestBodyArray).then((response: PoamResponsePut) => { + // Call the endpoint + updatePoam.updatePoamBySystemId(flags.systemId, requestBodyArray).then((response: PoamResponsePostPutDelete) => { console.log(colorize(outputFormat(response))) }).catch((error:any) => console.error(colorize(outputError(error)))) } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } } diff --git a/src/commands/emasser/put/software_baseline.ts b/src/commands/emasser/put/software_baseline.ts new file mode 100644 index 000000000..07aae04b4 --- /dev/null +++ b/src/commands/emasser/put/software_baseline.ts @@ -0,0 +1,430 @@ +/* eslint-disable valid-jsdoc */ + +import fs from 'fs' +import _ from 'lodash' +import {readFile} from 'fs/promises' +import {colorize} from 'json-colorizer' +import {Command, Flags} from '@oclif/core' + +import { + FlagOptions, + getFlagsForEndpoint, + getJsonExamples, + printRedMsg, +} from '../../../utils/emasser/utilities' +import {ApiConnection} from '../../../utils/emasser/apiConnection' +import {outputFormat} from '../../../utils/emasser/outputFormatter' +import {outputError} from '../../../utils/emasser/outputError' + +import {SoftwareBaselineApi} from '@mitre/emass_client' +import {SwBaselineResponsePostPut as SwBaselineResponse} from '@mitre/emass_client/dist/api' + +/** + * Represents a software entity with various attributes. + * + * @interface Software + * Required properties + * @property {string} [softwareId] - The unique identifier for the software. + * @property {string} [softwareVendor] - The vendor of the software. + * @property {string} [softwareName] - The name of the software. + * @property {string} [version] - The version of the software. + * Conditional property + * @property {number} [approvalDate] - The date when the software was approved. + * Optional properties + * @property {string} [softwareType] - The type of the software. + * @property {string} [parentSystem] - The parent system of the software.x + * @property {string} [subsystem] - The subsystem of the software.x + * @property {string} [network] - The network associated with the software.x + * @property {string} [hostingEnvironment] - The hosting environment of the software.x + * @property {string} [softwareDependencies] - The dependencies of the software.x + * @property {string} [cryptographicHash] - The cryptographic hash of the software.x + * @property {string} [inServiceData] - The in-service data of the software.x + * @property {string} [itBudgetUii] - The IT budget UII of the software.x + * @property {string} [fiscalYear] - The fiscal year associated with the software.x + * @property {string} [popEndDate] - The end date of the period of performance.x + * @property {string} [licenseOrContract] - The license or contract information of the software.x + * @property {string} [licenseTerm] - The term of the license.x + * @property {number} [costPerLicense] - The cost per license of the software.x + * @property {number} [totalLicenses] - The total number of licenses.x + * @property {number} [totalLicenseCost] - The total cost of all licenses.x + * @property {number} [licensesUsed] - The number of licenses used.x + * @property {string} [licensePoc] - The point of contact for the license.x + * @property {number} [licenseRenewalDate] - The date when the license needs to be renewed.x + * @property {number} [licenseExpirationDate] - The expiration date of the license.x + * @property {string} [approvalStatus] - The approval status of the software.x + * @property {number} [releaseDate] - The release date of the software.x + * @property {number} [maintenanceDate] - The maintenance date of the software.x + * @property {number} [retirementDate] - The retirement date of the software.x + * @property {number} [endOfLifeSupportDate] - The end-of-life support date of the software.x + * @property {number} [extendedEndOfLifeSupportDate] - The extended end-of-life support date of the software.x + * @property {boolean} [criticalAsset] - Indicates if the software is a critical asset.x + * @property {string} [location] - The location of the software.x + * @property {string} [purpose] - The purpose of the software.x + * Optional VA only properties + * @property {boolean} [unsupportedOperatingSystem] - Indicates if the software runs on an unsupported operating system. (VA Only) + * @property {boolean} [unapprovedSoftwareFromTrm] - Indicates if the software is unapproved from TRM. (VA Only) + * @property {boolean} [approvedWaiver] - Indicates if there is an approved waiver for the software. (VA Only) + */ +interface Software { + // Required field + softwareId?: string, + softwareVendor?: string, + softwareName?: string, + version?: string, + // Conditional Fields + approvalDate?: number, + // Optional Fields + softwareType?: string, + parentSystem?: string, + subsystem?: string, + network?: string, + hostingEnvironment?: string, + softwareDependencies?: string, + cryptographicHash?: string, + inServiceData?: string, + itBudgetUii?: string, + fiscalYear?: string, + popEndDate?:string, + licenseOrContract?: string, + licenseTerm?: string, + costPerLicense?: number, + totalLicenses?:number, + totalLicenseCost?:number, + licensesUsed?:number, + licensePoc?:string, + licenseRenewalDate?: number, + licenseExpirationDate?: number, + approvalStatus?: string, + releaseDate?: number, + maintenanceDate?: number, + retirementDate?: number, + endOfLifeSupportDate?: number, + extendedEndOfLifeSupportDate?: number, + criticalAsset?: boolean, + location?:string, + purpose?:string, + // VA Only + unsupportedOperatingSystem?: boolean, + unapprovedSoftwareFromTrm?: boolean, + approvedWaiver?: boolean, +} + +/** + * Combines JSON examples from multiple sources into a single object. + * + * This function aggregates JSON examples from three different sources: + * 'software-put-required', 'software-post-put-conditional', and 'software-post-put-optional'. + * It merges these examples into a single object and returns it as a string. + * + * @returns {string} A string representation of the combined JSON examples. + */ +function getAllJsonExamples(): string { + let exampleBodyObj: any = {} + + exampleBodyObj = { + ...getJsonExamples('software-put-required'), + ...getJsonExamples('software-post-put-conditional'), + ...getJsonExamples('software-post-put-optional'), + } + + return exampleBodyObj +} + +/** + * Asserts that a required parameter exists and is not undefined. + * + * @param object - The name of the parameter or field being checked. + * @param value - The value of the parameter or field to check. + * @throws Will throw an error if the value is undefined. + */ +function assertParamExists(object: string, value: string|undefined|null): void { + if (value === undefined) { + printRedMsg(`Missing required parameter/field: ${object}`) + throw new Error('Value not defined') + } +} + +/** + * Adds required fields to the request body for a software object. + * + * This function ensures that the required fields `softwareId`, `softwareVendor`, + * `softwareName`, and `version` are present in the input `dataObj`. If any of these + * fields are missing, an error is thrown and an example JSON structure is logged. + * + * @param dataObj - The software object containing the data to be validated and added to the request body. + * @returns A new software object containing only the required fields. + * @throws Will throw an error if any of the required fields are missing in `dataObj`. + */ +function addRequiredFieldsToRequestBody(dataObj: Software): Software { + const bodyObj: Software = {} + + try { + assertParamExists('softwareId', dataObj.softwareId) + assertParamExists('softwareVendor', dataObj.softwareVendor) + assertParamExists('softwareName', dataObj.softwareName) + assertParamExists('version', dataObj.version) + } catch (error) { + console.log('Required JSON fields are:') + console.log(colorize(JSON.stringify(getJsonExamples('software-put-required'), null, 2))) + throw error + } + + // The required parameter "systemId" is validated by oclif + bodyObj.softwareId = dataObj.softwareId + bodyObj.softwareVendor = dataObj.softwareVendor + bodyObj.softwareName = dataObj.softwareName + bodyObj.version = dataObj.version + + return bodyObj +} + +/** + * Adds conditional fields from the data object to the body object. + * + * @param bodyObject - The target object to which fields may be added. + * @param dataObj - The source object from which fields are conditionally copied. + */ +function addConditionalFields(bodyObject: Software, dataObj: Software): void { + if (Object.prototype.hasOwnProperty.call(dataObj, 'publicFacingFqdn')) { + bodyObject.approvalDate = dataObj.approvalDate + } +} + +/** + * Adds optional fields from the `dataObj` to the `bodyObject` if they exist. + * + * @param bodyObject - The target object to which optional fields will be added. + * @param dataObj - The source object containing optional fields. + */ +// skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold +function addOptionalFields(bodyObject: Software, dataObj: Software): void { + if (Object.prototype.hasOwnProperty.call(dataObj, 'softwareType')) { + bodyObject.softwareType = dataObj.softwareType + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'parentSystem')) { + bodyObject.parentSystem = dataObj.parentSystem + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'subsystem')) { + bodyObject.subsystem = dataObj.subsystem + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'network')) { + bodyObject.network = dataObj.network + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'hostingEnvironment')) { + bodyObject.hostingEnvironment = dataObj.hostingEnvironment + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'softwareDependencies')) { + bodyObject.softwareDependencies = dataObj.softwareDependencies + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'cryptographicHash')) { + bodyObject.cryptographicHash = dataObj.cryptographicHash + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'inServiceData')) { + bodyObject.inServiceData = dataObj.inServiceData + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'itBudgetUii')) { + bodyObject.itBudgetUii = dataObj.itBudgetUii + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'fiscalYear')) { + bodyObject.fiscalYear = dataObj.fiscalYear + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'popEndDate')) { + bodyObject.popEndDate = dataObj.popEndDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licenseOrContract')) { + bodyObject.licenseOrContract = dataObj.licenseOrContract + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licenseTerm')) { + bodyObject.licenseTerm = dataObj.licenseTerm + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'costPerLicense')) { + bodyObject.costPerLicense = dataObj.costPerLicense + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'totalLicenses')) { + bodyObject.totalLicenses = dataObj.totalLicenses + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'totalLicenseCost')) { + bodyObject.totalLicenseCost = dataObj.totalLicenseCost + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licensesUsed')) { + bodyObject.licensesUsed = dataObj.licensesUsed + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licensePoc')) { + bodyObject.licensePoc = dataObj.licensePoc + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licenseRenewalDate')) { + bodyObject.licenseRenewalDate = dataObj.licenseRenewalDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'licenseExpirationDate ')) { + bodyObject.licenseExpirationDate = dataObj.licenseExpirationDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'approvalStatus')) { + bodyObject.approvalStatus = dataObj.approvalStatus + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'releaseDate')) { + bodyObject.releaseDate = dataObj.releaseDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'maintenanceDate')) { + bodyObject.maintenanceDate = dataObj.maintenanceDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'retirementDate')) { + bodyObject.retirementDate = dataObj.retirementDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'endOfLifeSupportDate')) { + bodyObject.endOfLifeSupportDate = dataObj.endOfLifeSupportDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'extendedEndOfLifeSupportDate')) { + bodyObject.extendedEndOfLifeSupportDate = dataObj.extendedEndOfLifeSupportDate + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'criticalAsset')) { + bodyObject.criticalAsset = dataObj.criticalAsset + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'location')) { + bodyObject.location = dataObj.location + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'purpose')) { + bodyObject.purpose = dataObj.purpose + } + + // VA Only + if (Object.prototype.hasOwnProperty.call(dataObj, 'unsupportedOperatingSystem')) { + bodyObject.unsupportedOperatingSystem = dataObj.unsupportedOperatingSystem + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'unapprovedSoftwareFromTrm')) { + bodyObject.unapprovedSoftwareFromTrm = dataObj.unapprovedSoftwareFromTrm + } + + if (Object.prototype.hasOwnProperty.call(dataObj, 'approvedWaiver')) { + bodyObject.approvedWaiver = dataObj.approvedWaiver + } +} + +/** + * Generates a body object for a software baseline request. + * + * This function takes a `Software` object as input and constructs a new `Software` + * object by adding required, conditional, and optional fields to it. If an error + * occurs during this process, the function will terminate the process with an exit code of 1. + * + * @param dataObject - The input `Software` object containing the initial data. + * @returns The constructed `Software` object with the necessary fields added. + */ +function generateBodyObj(dataObject: Software): Software { + let bodyObj: Software = {} + + try { + bodyObj = addRequiredFieldsToRequestBody(dataObject) + addConditionalFields(bodyObj, dataObject) + addOptionalFields(bodyObj, dataObject) + } catch { + process.exit(1) + } + + return bodyObj +} + +const CMD_HELP = 'saf emasser put software_baseline -h or --help' +export default class EmasserSoftwareBaseline extends Command { + static readonly usage = '<%= command.id %> [FLAGS]\n\x1B[93m NOTE: see EXAMPLES for command usages\x1B[0m' + + static readonly description = 'Update one or many software assets to a system.\n' + + 'The CLI expects an input JSON file containing the required, conditional\n' + + 'and optional fields for the software asset(s) being added to the system.' + + static readonly examples = [ + '<%= config.bin %> <%= command.id %> [-s,--systemId] [-f,--dataFile]', + 'The input file should be a well formed JSON containing Software Assets.', + '\x1B[1mRequired JSON parameter/field is:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('software-put-required'), null, 2)), + '\x1B[1mConditional JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('software-post-put-conditional'), null, 2)), + '\x1B[1mOptional JSON parameters/fields are:\x1B[0m', + colorize(JSON.stringify(getJsonExamples('software-post-put-optional'), null, 2)), + '\x1B[1m\x1B[32mAll accepted parameters/fields are:\x1B[0m', + colorize(getAllJsonExamples()), + ] + + static readonly flags = { + help: Flags.help({char: 'h', description: 'Show eMASSer CLI help for the PUT Software Baseline command'}), + ...getFlagsForEndpoint(process.argv) as FlagOptions, // skipcq: JS-0349 + } + + async run(): Promise { + const {flags} = await this.parse(EmasserSoftwareBaseline) + const apiCxn = new ApiConnection() + const swBaseline = new SoftwareBaselineApi(apiCxn.configuration, apiCxn.basePath, apiCxn.axiosInstances) + + const requestBodyArray: Software[] = [] + + // Check if a Software json file was provided + if (fs.existsSync(flags.dataFile)) { + let data: any + try { + data = JSON.parse(await readFile(flags.dataFile, 'utf8')) + } catch (error: any) { + console.error('\x1B[91m» Error reading Software data file, possible malformed json. Please use the -h flag for help.\x1B[0m') + console.error('\x1B[93m→ Error message was:', error.message, '\x1B[0m') + process.exit(1) + } + + // Process the Software data file + if (Array.isArray(data)) { + data.forEach((dataObject: Software) => { + // Generate the put request object + requestBodyArray.push(generateBodyObj(dataObject)) + }) + } else if (typeof data === 'object') { + const dataObject: Software = data + // Generate the put request object + requestBodyArray.push(generateBodyObj(dataObject)) + } + } else { + console.error('\x1B[91m» Software data file (.json) not found or invalid:', flags.dataFile, '\x1B[0m') + process.exit(1) + } + + // Call the endpoint + swBaseline.updateSwBaselineAssets(flags.systemId, requestBodyArray).then((response: SwBaselineResponse) => { + console.log(colorize(outputFormat(response, false))) + }).catch((error: any) => console.error(colorize(outputError(error)))) + } + + protected async catch(err: Error & {exitCode?: number}): Promise { // skipcq: JS-0116 + // If error message is for missing flags, display + // what fields are required, otherwise show the error + if (err.message.includes('See more help with --help')) { + this.warn(err.message.replace('with --help', `with: \x1B[93m${CMD_HELP}\x1B[0m`)) + } else { + this.warn(err) + } + } +} diff --git a/src/commands/generate/delta.ts b/src/commands/generate/delta.ts index 2efe692ee..551fba13e 100644 --- a/src/commands/generate/delta.ts +++ b/src/commands/generate/delta.ts @@ -9,13 +9,9 @@ import { updateProfileUsingXCCDF, processXCCDF, updateControl, + Profile, + Control, } from '@mitre/inspec-objects' - -// eslint-disable-next-line no-warning-comments -// TODO: We shouldn't have to import like this, open issue to clean library up for inspec-objects -// test failed in updating inspec-objects to address high lvl vuln -import Profile from '@mitre/inspec-objects/lib/objects/profile' -import Control from '@mitre/inspec-objects/lib/objects/control' import path from 'path' import {createWinstonLogger} from '../../utils/logging' import fse from 'fs-extra' diff --git a/src/commands/generate/inspec_profile.ts b/src/commands/generate/inspec_profile.ts index 9ba14c877..d97825e4b 100644 --- a/src/commands/generate/inspec_profile.ts +++ b/src/commands/generate/inspec_profile.ts @@ -1,15 +1,13 @@ import {Flags} from '@oclif/core' import fs from 'fs' -import parser from 'fast-xml-parser' +import {XMLParser} from 'fast-xml-parser' import {InSpecMetaData, InspecReadme} from '../../types/inspec' import path from 'path' import {createWinstonLogger} from '../../utils/logging' -import {processOVAL, processXCCDF} from '@mitre/inspec-objects' -import Profile from '@mitre/inspec-objects/lib/objects/profile' +import {processOVAL, processXCCDF, Profile} from '@mitre/inspec-objects' import {BaseCommand} from '../../utils/oclif/baseCommand' import {Logger} from 'winston' import _ from 'lodash' -import YAML from 'yaml' export default class InspecProfile extends BaseCommand { static readonly usage = @@ -112,7 +110,7 @@ export default class InspecProfile extends BaseCommand { ignoreAttributes: false, attributeNamePrefix: '@_', } - const xmlDoc = new parser.XMLParser(options).parse(xccdf) + const xmlDoc = new XMLParser(options).parse(xccdf) let outDir = '' if (flags.output === 'profile') { const benchmarkTitle = (benchmarkType === 'cis') ? @@ -629,25 +627,7 @@ ${contentObj.profileType === 'CIS' ? } function generateYaml(profile: Profile, outDir: string, logger: Logger) { - // ---------------------------------------------------------------------- - // NOTE: Not using the profile.createInspecYaml() as it does not wrap the - // inspect_version in double quotes (the format is ~>#.#). Use this - // function until ts-object.ts method is fixed - - const inspecYmlContent = YAML.stringify({ - name: profile.name, - title: profile.title, - maintainer: profile.maintainer, - copyright: profile.copyright, - copyright_email: profile.copyright_email, - license: profile.license, - summary: profile.summary, - description: profile.description, - version: profile.version, - supports: profile.supports, - depends: profile.depends, - inspec_version: YAML.stringify(`${profile.inspec_version}`, {defaultStringType: 'QUOTE_DOUBLE'}), - }) + + const inspecYmlContent = profile.createInspecYaml() + ` ### INPUTS ### diff --git a/src/commands/generate/update_controls4delta.ts b/src/commands/generate/update_controls4delta.ts index ace7244bd..2c301c36f 100644 --- a/src/commands/generate/update_controls4delta.ts +++ b/src/commands/generate/update_controls4delta.ts @@ -4,11 +4,11 @@ import {readdir} from 'fs/promises' import {execSync} from 'child_process' import {Flags} from '@oclif/core' import {createWinstonLogger} from '../../utils/logging' -import Profile from '@mitre/inspec-objects/lib/objects/profile' import { getExistingDescribeFromControl, processInSpecProfile, processXCCDF, + Profile, } from '@mitre/inspec-objects' import colors from 'colors' // eslint-disable-line no-restricted-imports import {BaseCommand} from '../../utils/oclif/baseCommand' diff --git a/src/commands/validate/threshold.ts b/src/commands/validate/threshold.ts index 20990403d..d6f1deecb 100644 --- a/src/commands/validate/threshold.ts +++ b/src/commands/validate/threshold.ts @@ -1,8 +1,9 @@ -import {Flags} from '@oclif/core' -import YAML from 'yaml' import fs from 'fs' -import {ContextualizedProfile, convertFileContextual} from 'inspecjs' import _ from 'lodash' +import YAML from 'yaml' +import {expect} from 'chai' +import {Flags} from '@oclif/core' +import {ContextualizedProfile, convertFileContextual} from 'inspecjs' import {ThresholdValues} from '../../types/threshold' import {calculateCompliance, exitNonZeroIfTrue, @@ -13,7 +14,6 @@ import {calculateCompliance, statusSeverityPaths, totalMax, totalMin} from '../../utils/threshold' -import {expect} from 'chai' import {BaseCommand} from '../../utils/oclif/baseCommand' let flat: any @@ -51,7 +51,7 @@ export default class Threshold extends BaseCommand { }), } - async run() { + async run() { // skipcq: JS-R1005 const {flags} = await this.parse(Threshold) let thresholds: ThresholdValues = {} if (flags.templateInline) { @@ -75,11 +75,15 @@ export default class Threshold extends BaseCommand { const parsedExecJSON = convertFileContextual(fs.readFileSync(flags.input, 'utf8')) const overallStatusCounts = extractStatusCounts(parsedExecJSON.contains[0] as ContextualizedProfile) - if (thresholds.compliance) { const overallCompliance = calculateCompliance(overallStatusCounts) - exitNonZeroIfTrue(Boolean(thresholds.compliance.min && overallCompliance < thresholds.compliance.min), 'Overall compliance minimum was not satisfied') // Compliance Minimum - exitNonZeroIfTrue(Boolean(thresholds.compliance.max && overallCompliance > thresholds.compliance.max), 'Overall compliance maximum was not satisfied') // Compliance Maximum + try { + exitNonZeroIfTrue(Boolean(thresholds.compliance.min && overallCompliance < thresholds.compliance.min), 'Overall compliance minimum was not satisfied') // Compliance Minimum + exitNonZeroIfTrue(Boolean(thresholds.compliance.max && overallCompliance > thresholds.compliance.max), 'Overall compliance maximum was not satisfied') // Compliance Maximum + } catch { + process.exitCode = 1 + return + } } // Total Pass/Fail/Skipped/No Impact/Error @@ -87,39 +91,54 @@ export default class Threshold extends BaseCommand { for (const statusThreshold of targets) { const [statusName, _total] = statusThreshold.split('.') if (_.get(thresholds, statusThreshold) !== undefined && typeof _.get(thresholds, statusThreshold) !== 'object') { - exitNonZeroIfTrue( - Boolean( - _.get(overallStatusCounts, renameStatusName(statusName)) !== - _.get(thresholds, statusThreshold), - ), - `${statusThreshold}: Threshold not met. Number of received total ${statusThreshold.split('.')[0]} controls (${_.get(overallStatusCounts, renameStatusName(statusName))}) is not equal to your set threshold for the number of ${statusThreshold.split('.')[0]} controls (${_.get(thresholds, statusThreshold)})`, - ) + try { + exitNonZeroIfTrue( + Boolean( + _.get(overallStatusCounts, renameStatusName(statusName)) !== + _.get(thresholds, statusThreshold), + ), + `${statusThreshold}: Threshold not met. Number of received total ${statusThreshold.split('.')[0]} controls (${_.get(overallStatusCounts, renameStatusName(statusName))}) is not equal to your set threshold for the number of ${statusThreshold.split('.')[0]} controls (${_.get(thresholds, statusThreshold)})`, + ) + } catch { + process.exitCode = 1 + return + } } } for (const totalMinimum of totalMin) { const [statusName] = totalMinimum.split('.') if (_.get(thresholds, totalMinimum) !== undefined) { - exitNonZeroIfTrue( - Boolean( - _.get(overallStatusCounts, renameStatusName(statusName)) < - _.get(thresholds, totalMinimum), - ), - `${totalMinimum}: Threshold not met. Number of received total ${totalMinimum.split('.')[0]} controls (${_.get(overallStatusCounts, renameStatusName(statusName))}) is less than your set threshold for the number of ${totalMinimum.split('.')[0]} controls (${_.get(thresholds, totalMinimum)})`, - ) + try { + exitNonZeroIfTrue( + Boolean( + _.get(overallStatusCounts, renameStatusName(statusName)) < + _.get(thresholds, totalMinimum), + ), + `${totalMinimum}: Threshold not met. Number of received total ${totalMinimum.split('.')[0]} controls (${_.get(overallStatusCounts, renameStatusName(statusName))}) is less than your set threshold for the number of ${totalMinimum.split('.')[0]} controls (${_.get(thresholds, totalMinimum)})`, + ) + } catch { + process.exitCode = 1 + return + } } } for (const totalMaximum of totalMax) { const [statusName] = totalMaximum.split('.') if (_.get(thresholds, totalMaximum) !== undefined) { - exitNonZeroIfTrue( - Boolean( - _.get(overallStatusCounts, renameStatusName(statusName)) > - _.get(thresholds, totalMaximum), - ), - `${totalMaximum}: Threshold not met. Number of received total ${totalMaximum.split('.')[0]} controls (${_.get(overallStatusCounts, renameStatusName(statusName))}) is greater than your set threshold for the number of ${totalMaximum.split('.')[0]} controls (${_.get(thresholds, totalMaximum)})`, - ) + try { + exitNonZeroIfTrue( + Boolean( + _.get(overallStatusCounts, renameStatusName(statusName)) > + _.get(thresholds, totalMaximum), + ), + `${totalMaximum}: Threshold not met. Number of received total ${totalMaximum.split('.')[0]} controls (${_.get(overallStatusCounts, renameStatusName(statusName))}) is greater than your set threshold for the number of ${totalMaximum.split('.')[0]} controls (${_.get(thresholds, totalMaximum)})`, + ) + } catch { + process.exitCode = 1 + return + } } } @@ -129,19 +148,29 @@ export default class Threshold extends BaseCommand { for (const statusCountThreshold of targetPaths) { const [statusName, _total, thresholdType] = statusCountThreshold.split('.') if (thresholdType === 'min' && _.get(thresholds, statusCountThreshold) !== undefined) { - exitNonZeroIfTrue( - Boolean( - _.get(criticalStatusCounts, renameStatusName(statusName)) < _.get(thresholds, statusCountThreshold), - ), - `${statusCountThreshold}: Threshold not met. Number of received total ${statusCountThreshold.split('.')[0]} controls (${_.get(criticalStatusCounts, renameStatusName(statusName))}) is less than your set threshold for the number of ${statusCountThreshold.split('.')[0]} controls (${_.get(thresholds, statusCountThreshold)})`, - ) + try { + exitNonZeroIfTrue( + Boolean( + _.get(criticalStatusCounts, renameStatusName(statusName)) < _.get(thresholds, statusCountThreshold), + ), + `${statusCountThreshold}: Threshold not met. Number of received total ${statusCountThreshold.split('.')[0]} controls (${_.get(criticalStatusCounts, renameStatusName(statusName))}) is less than your set threshold for the number of ${statusCountThreshold.split('.')[0]} controls (${_.get(thresholds, statusCountThreshold)})`, + ) + } catch { + process.exitCode = 1 + return + } } else if (thresholdType === 'max' && _.get(thresholds, statusCountThreshold) !== undefined) { - exitNonZeroIfTrue( - Boolean( - _.get(criticalStatusCounts, renameStatusName(statusName)) > _.get(thresholds, statusCountThreshold), - ), - `${statusCountThreshold}: Threshold not met. Number of received total ${statusCountThreshold.split('.')[0]} controls (${_.get(criticalStatusCounts, renameStatusName(statusName))}) is greater than your set threshold for the number of ${statusCountThreshold.split('.')[0]} controls (${_.get(thresholds, statusCountThreshold)})`, - ) + try { + exitNonZeroIfTrue( + Boolean( + _.get(criticalStatusCounts, renameStatusName(statusName)) > _.get(thresholds, statusCountThreshold), + ), + `${statusCountThreshold}: Threshold not met. Number of received total ${statusCountThreshold.split('.')[0]} controls (${_.get(criticalStatusCounts, renameStatusName(statusName))}) is greater than your set threshold for the number of ${statusCountThreshold.split('.')[0]} controls (${_.get(thresholds, statusCountThreshold)})`, + ) + } catch { + process.exitCode = 1 + return + } } } } @@ -157,14 +186,24 @@ export default class Threshold extends BaseCommand { try { expect(actualControlIds).to.contain(expectedControlId) } catch { - exitNonZeroIfTrue(true, `Expected ${targetPath} to contain ${expectedControlId} controls but it only contained [${actualControlIds?.join(', ')}]`) // Chai doesn't print the actual object diff anymore + try { + exitNonZeroIfTrue(true, `Expected ${targetPath} to contain ${expectedControlId} controls but it only contained [${actualControlIds?.join(', ')}]`) // Chai doesn't print the actual object diff anymore + } catch { + process.exitCode = 1 + return + } } } try { expect(expectedControlIds.length).to.equal(actualControlIds?.length) } catch { - exitNonZeroIfTrue(true, `Expected ${targetPath} to contain ${expectedControlIds.length} controls but it contained ${actualControlIds?.length}`) + try { + exitNonZeroIfTrue(true, `Expected ${targetPath} to contain ${expectedControlIds.length} controls but it contained ${actualControlIds?.length}`) + } catch { + process.exitCode = 1 + return + } } } } diff --git a/src/utils/emasser/apiConfig.ts b/src/utils/emasser/apiConfig.ts index 1a67714a7..47ea531f5 100644 --- a/src/utils/emasser/apiConfig.ts +++ b/src/utils/emasser/apiConfig.ts @@ -1,26 +1,48 @@ import fs from 'fs' import dotenv from 'dotenv' - -function printYellowMsg(msg: string) { - console.log('\x1B[93m', msg, '\x1B[0m') -} - -function printRedMsg(msg: string) { - console.log('\x1B[91m', msg, '\x1B[0m') -} +import {printHelpMsg, printRedMsg} from './utilities' function printHelpMessage() { - printYellowMsg('Use the emasser CLI command "saf emasser configure" to generate or update an eMASS configuration file.') - printYellowMsg('If the configuration file is generated, it is placed in the directory where the emasser command is executed.') + printHelpMsg('Use the eMASSer CLI command "saf emasser configure" to generate or update an eMASS configuration file.') + printHelpMsg('If a configuration file exists, it is placed in the directory where the emasser command is executed.') } +// eslint-disable-next-line valid-jsdoc +/** + * The `ApiConfig` class is responsible for loading and managing the configuration + * settings required for connecting to the eMASS API. It reads environment variables + * from a `.env` file and provides methods to retrieve required and optional configuration + * values. + * + * @class ApiConfig + * @property {string} url - The URL of the eMASS API host. + * @property {number | any} port - The port number for the eMASS API. + * @property {string | undefined} caCert - The path to the CA certificate file. + * @property {string | undefined} keyCert - The path to the key certificate file. + * @property {string | undefined} clientCert - The path to the client certificate file. + * @property {string} apiPassPhrase - The passphrase for the API key file. + * @property {string} apiKey - The API key for authenticating requests. + * @property {string} userUid - The user UID required for actionable requests. + * @property {boolean} sslVerify - Whether to verify SSL certificates. + * @property {boolean} reqCert - Whether to request a certificate. + * @property {string} debugging - Debugging mode flag. + * @property {string} displayNulls - Flag to display null values. + * @property {string} displayDateTime - Flag to display date and time. + * @property {string} downloadDir - The directory for downloads. + * + * @constructor + * Initializes a new instance of the `ApiConfig` class. Loads environment variables + * from a `.env` file and sets the configuration properties. If required environment + * variables are missing, it prints an error message and exits the process. + */ export class ApiConfig { private envConfig: {[key: string]: string | undefined}; public url: string; public port: number|any; - public keyCert: string; - public clientCert: string; + public caCert: string | undefined; + public keyCert: string | undefined; + public clientCert: string | undefined; public apiPassPhrase: string; public apiKey: string; public userUid: string; @@ -29,6 +51,7 @@ export class ApiConfig { public debugging: string; public displayNulls: string; public displayDateTime: string; + public downloadDir: string; constructor() { try { @@ -46,20 +69,20 @@ export class ApiConfig { } // Option Environment Variable + // The userUid is required by some eMASS instances for actionable requests (post,put,delete) + this.userUid = this.getOptionalEnv('EMASSER_USER_UID', '') this.port = this.getOptionalEnv('EMASSER_PORT', 443) this.sslVerify = this.getOptionalEnv('EMASSER_REJECT_UNAUTHORIZED', false) this.reqCert = this.getOptionalEnv('EMASSER_REQUEST_CERT', false) this.debugging = this.getOptionalEnv('EMASSER_DEBUGGING', false) this.displayNulls = this.getOptionalEnv('EMASSER_CLI_DISPLAY_NULL', true) this.displayDateTime = this.getOptionalEnv('EMASSER_EPOCH_TO_DATETIME', false) + this.downloadDir = this.getOptionalEnv('EMASSER_DOWNLOAD_DIR', 'eMASSerDownloads') // Required Environment Variables try { this.apiKey = this.getRequiredEnv('EMASSER_API_KEY') - this.userUid = this.getRequiredEnv('EMASSER_USER_UID') this.url = this.getRequiredEnv('EMASSER_HOST_URL') - this.keyCert = this.getRequiredEnv('EMASSER_KEY_FILE_PATH') - this.clientCert = this.getRequiredEnv('EMASSER_CERT_FILE_PATH') this.apiPassPhrase = this.getRequiredEnv('EMASSER_KEY_FILE_PASSWORD') } catch (error: any) { if (error.name === 'EVNF') { @@ -70,8 +93,32 @@ export class ApiConfig { process.exit(0) } + + // Get provided certificate(s). Require either a .pfx certificate, + // or a client and key .pem certificates + if (Object.prototype.hasOwnProperty.call(this.envConfig, 'EMASSER_CA_FILE_PATH') && this.envConfig.EMASSER_CA_FILE_PATH !== '') { + this.caCert = this.envConfig.EMASSER_CA_FILE_PATH + } else if (Object.prototype.hasOwnProperty.call(this.envConfig, 'EMASSER_KEY_FILE_PATH') && this.envConfig.EMASSER_KEY_FILE_PATH !== '' && + Object.prototype.hasOwnProperty.call(this.envConfig, 'EMASSER_CERT_FILE_PATH') && this.envConfig.EMASSER_CERT_FILE_PATH !== '') { + // We have the .pem certificate files + this.keyCert = this.envConfig.EMASSER_KEY_FILE_PATH + this.clientCert = this.envConfig.EMASSER_CERT_FILE_PATH + } else { + // We don't have neither a .pfx or .pem(s) certificates + printRedMsg('A CA certificate (.cer/.crt/.pem) or a Key and Client (.pem) certificate files were expected') + printRedMsg('If providing PEM certificates, the required certs are "key.pem" and "client.pem"') + process.exit(0) + } } + /** + * Retrieves the value of the specified environment variable from the configuration. + * If the environment variable is not found, an error is thrown. + * + * @param {string} key - The key of the environment variable to retrieve. + * @returns {string | any} - The value of the environment variable. + * @throws {Error} - Throws an error if the environment variable is not found. + */ getRequiredEnv(key: string): string | any { if (Object.prototype.hasOwnProperty.call(this.envConfig, key)) { return this.envConfig[key] @@ -83,6 +130,13 @@ export class ApiConfig { throw err } + /** + * Retrieves the value of an environment variable if it exists, otherwise returns a default value. + * + * @param key - The key of the environment variable to retrieve. + * @param defaultValue - The default value to return if the environment variable does not exist. + * @returns The value of the environment variable if it exists, otherwise the default value. + */ getOptionalEnv(key: string, defaultValue: any): string | any { if (Object.prototype.hasOwnProperty.call(this.envConfig, key)) { return this.envConfig[key] diff --git a/src/utils/emasser/apiConnection.ts b/src/utils/emasser/apiConnection.ts index 819ea7fef..6fe7100f9 100644 --- a/src/utils/emasser/apiConnection.ts +++ b/src/utils/emasser/apiConnection.ts @@ -1,8 +1,22 @@ import {Configuration} from '@mitre/emass_client/dist/configuration' -import {AxiosInstance} from '@mitre/emass_client/node_modules/axios' +import {AxiosInstance} from 'axios' import {ApiConfig} from './apiConfig' import {InitConnections} from './initConnection' +/** + * Class representing an API connection. + * + * @remarks + * This class initializes the API connection using the provided configuration and axios instances. + * + * @example + * ```typescript + * const apiConnection = new ApiConnection(); + * console.log(apiConnection.configuration); + * console.log(apiConnection.basePath); + * console.log(apiConnection.axiosInstances); + * ``` + */ export class ApiConnection { public configuration: Configuration; public basePath: Configuration['basePath']; diff --git a/src/utils/emasser/generateConfig.ts b/src/utils/emasser/generateConfig.ts index cce3b0056..fa9f905c8 100644 --- a/src/utils/emasser/generateConfig.ts +++ b/src/utils/emasser/generateConfig.ts @@ -1,42 +1,52 @@ +/* eslint-disable valid-jsdoc */ import fse from 'fs-extra' import dotenv from 'dotenv' import _ from 'lodash' // eslint-disable-next-line no-restricted-imports import colors from 'colors' // eslint-disable-next-line node/no-extraneous-import -import {input, password, select} from '@inquirer/prompts' +import {input, confirm, password, select} from '@inquirer/prompts' const PROMPT_MESSAGE = [ 'Provide the eMASS API key (EMASSER_API_KEY) - valid key is > 30 alpha numeric characters:', - 'Provide the eMASS User unique identifier (EMASSER_USER_UID):', 'Provide the eMASS server FQDN (EMASSER_HOST_URL) :', 'Provide the eMASS private encrypting file (key.pem) - include the path (EMASSER_KEY_FILE_PATH)):', 'Provide the eMASS client certificate file (cert.pem) - include the path (EMASSER_CERT_FILE_PATH):', - 'Provide the password for the private encryption key.pem file (EMASSER_KEY_FILE_PASSWORD):', + 'Provide the eMASS CA certificate file (.cer, crt, or .pem) - include the path (EMASSER_CA_FILE_PATH):', + 'Provide the secret phrase used to protect the encryption key:', + 'Provide the eMASS User unique identifier (EMASSER_USER_UID):', 'Provide the server communication port number (default is 443):', 'Server requests a certificate from connecting clients - true or false (default true):', 'Reject clients with invalid certificates - true or false (default true):', 'Set debugging on (true) or off (false) (default false):', 'Display null value fields - true or false (default false):', 'Convert epoch to data/time value - true or false (default true):', + 'Directory where exported files are saved (default eMASSerDownloads):', ] const PROMPT_NAMES_REQUIRED = [ + '# -----------------------------------------------------------------------------', + '# Required environment variables', 'EMASSER_API_KEY', - 'EMASSER_USER_UID', 'EMASSER_HOST_URL', 'EMASSER_KEY_FILE_PATH', 'EMASSER_CERT_FILE_PATH', + 'EMASSER_CA_FILE_PATH', 'EMASSER_KEY_FILE_PASSWORD', + '# Not required by certain eMASS instances (required by most)', + 'EMASSER_USER_UID', ] const PROMPT_NAMES_OPTIONAL = [ + '# -----------------------------------------------------------------------------', + '# Optional environment variables', 'EMASSER_PORT', 'EMASSER_REQUEST_CERT', 'EMASSER_REJECT_UNAUTHORIZED', 'EMASSER_DEBUGGING', 'EMASSER_CLI_DISPLAY_NULL', 'EMASSER_EPOCH_TO_DATETIME', + 'EMASSER_DOWNLOAD_DIR', ] const OPTIONAL_DEFAULT_VALUES = [ @@ -46,21 +56,58 @@ const OPTIONAL_DEFAULT_VALUES = [ false, true, false, + "'eMASSerDownloads'", ] +/** + * Generates a new `.env` file with required and optional environment variables. + * + * This function constructs the content for a new `.env` file by iterating over + * the `PROMPT_NAMES_REQUIRED` and `PROMPT_NAMES_OPTIONAL` arrays. Required variables + * are added with empty values, while optional variables are added with their respective + * default values from the `OPTIONAL_DEFAULT_VALUES` array. + * + * The generated content is then written to a file named `.env` in the current directory. + * + * @remarks + * - Lines starting with `#` are treated as comments and are added as-is. + * - The `OPTIONAL_DEFAULT_VALUES` array is expected to have a length that matches the + * `PROMPT_NAMES_OPTIONAL` array, with the first two elements being placeholders. + * + * @throws Will throw an error if the file cannot be written. + */ function generateNewdotEnv() { - // data contains the .env variables with optional default values. let data = '' PROMPT_NAMES_REQUIRED.forEach(element => { - data = data + element + '=\n' + data += element.startsWith('#') ? element + '\n' : element + "=''\n" }) + PROMPT_NAMES_OPTIONAL.forEach((element, index) => { - data = data + element + '=' + OPTIONAL_DEFAULT_VALUES[index] + '\n' + data += element.startsWith('#') ? element + '\n' : element + '=' + OPTIONAL_DEFAULT_VALUES[index - 2] + '\n' }) fse.writeFileSync('.env', data) } +/** + * Asynchronously processes user prompts to generate and update an eMASS configuration file. + * + * This function performs the following steps: + * 1. Parses the `.env` file to retrieve existing environment variables. + * 2. Dynamically imports `inquirer-file-selector` and `chalk` modules. + * 3. Initializes a theme for the file selector prompts. + * 4. Prompts the user for required eMASS configuration variables such as API key, host URL, and key file password. + * 5. Prompts the user to select the type of certificate to use (Key/Client Certificate or Single CA Certificate). + * 6. Based on the selected certificate type, prompts the user to select the appropriate certificate files. + * 7. Optionally prompts the user to add a unique user identifier. + * 8. Prompts the user for optional eMASS configuration variables such as port, request certificate, reject unauthorized, debugging, CLI display null, epoch to datetime, and download directory. + * 9. Updates the `.env` file with the collected configuration variables. + * 10. Outputs the content of the updated `.env` file to the console. + * + * @async + * @function processPrompt + * @returns {Promise} A promise that resolves when the prompts have been processed and the `.env` file has been updated. + */ async function processPrompt() { const envConfig = dotenv.parse(fse.readFileSync('.env')) @@ -76,75 +123,140 @@ async function processPrompt() { }, } - const answers = { + // Variable used to store the prompts (question and answers) + const interactiveValues: {[key: string]: any} = {} + // Reset the certificates as the user will choose what cert type to use + interactiveValues.EMASSER_KEY_FILE_PATH = '' + interactiveValues.EMASSER_CERT_FILE_PATH = '' + interactiveValues.EMASSER_CA_FILE_PATH = '' + + // Required variables + const requiredContent = { EMASSER_API_KEY: await input({ message: PROMPT_MESSAGE[0], default: envConfig.EMASSER_API_KEY, - validate(input: string) { + required: true, + validate: (input: string) => { if (/([a-zA-Z0-9-]{30,})/g.test(input)) { // skipcq: JS-0113 return true } - throw new Error('Invalid API key. Must have more than 30 alpha numeric characters, no special keys other than a dash(-)') + return 'Invalid API key. Must have more than 30 alpha numeric characters, no special keys other than a dash(-)' }, }), - EMASSER_USER_UID: await input({ - message: PROMPT_MESSAGE[1], - default: envConfig.EMASSER_USER_UID, - }), EMASSER_HOST_URL: await input({ - message: PROMPT_MESSAGE[2], + message: PROMPT_MESSAGE[1], default: envConfig.EMASSER_HOST_URL, - validate(input: string) { + required: true, + validate: (input: string) => { // eslint-disable-next-line no-useless-escape if (/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/g.test(input)) { // skipcq: JS-0113, JS-0097 return true } - throw new Error('Invalid eMASS FQDN (URL). Format: [protocol]://[hostname].[...].[domain].') + return 'Invalid eMASS FQDN (URL). Format: [protocol]://[hostname].[...].[domain].' }, }), + EMASSER_KEY_FILE_PASSWORD: await password({ + message: PROMPT_MESSAGE[5], + mask: true, + }), + } + + // Add required content to the collection + // eslint-disable-next-line guard-for-in + for (const tagName in requiredContent) { + const answerValue = _.get(requiredContent, tagName) + if (answerValue !== null) { + interactiveValues[tagName] = answerValue + } + } + + // What type od cert are we using? + const certTypes = { + certType: await select({ + message: 'What type of certificate to use with eMASS!', + default: 'Key_Cert', + choices: [ + {name: 'Key/Client Certificate', value: 'key_cert'}, + {name: 'Single CA Certificate', value: 'CA'}, + ], + }), + } + + // Get cert information + const requiredCerts = certTypes.certType === 'key_cert' ? { EMASSER_KEY_FILE_PATH: await fileSelector({ - message: PROMPT_MESSAGE[3], + message: PROMPT_MESSAGE[2], pageSize: 15, loop: true, type: 'file', allowCancel: true, - cancelText: 'No Key (.pem) file was selected', emptyText: 'Directory is empty', showExcluded: false, filter: file => file.isDirectory() || file.name.endsWith('.pem'), theme: fileSelectorTheme, }), EMASSER_CERT_FILE_PATH: await fileSelector({ - message: PROMPT_MESSAGE[4], + message: PROMPT_MESSAGE[3], pageSize: 15, loop: true, type: 'file', allowCancel: true, - cancelText: 'No Client (.pem) file was selected', emptyText: 'Directory is empty', showExcluded: false, filter: file => file.isDirectory() || file.name.endsWith('.pem'), theme: fileSelectorTheme, }), - EMASSER_KEY_FILE_PASSWORD: await password({ - message: PROMPT_MESSAGE[5], - mask: true, + } : { + EMASSER_CA_FILE_PATH: await fileSelector({ + message: PROMPT_MESSAGE[4], + pageSize: 15, + loop: true, + type: 'file', + allowCancel: true, + emptyText: 'Directory is empty', + showExcluded: false, + filter: file => file.isDirectory() || file.name.endsWith('.pem') || file.name.endsWith('.crt') || file.name.endsWith('.cer'), + theme: fileSelectorTheme, }), - EMASSER_PORT: await input({ + } + + // Add certs content to the collection + // eslint-disable-next-line guard-for-in + for (const tagName in requiredCerts) { + const answerValue = _.get(requiredCerts, tagName) + if (answerValue !== null) { + interactiveValues[tagName] = answerValue + } + } + + // Use the unique user identifier? + const addUserUid = await confirm({message: 'Add a unique user identifier (user-id)?'}) + if (addUserUid) { + const EMASSER_USER_UID = await input({ message: PROMPT_MESSAGE[6], + default: envConfig.EMASSER_USER_UID, + required: true, + }) + interactiveValues.EMASSER_USER_UID = EMASSER_USER_UID + } + + // Process the optional environment configuration variables + const optionalContent = { + EMASSER_PORT: await input({ + message: PROMPT_MESSAGE[7], default: envConfig.EMASSER_PORT, validate(input: string) { if (/(^([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$)/g.test(input)) { // skipcq: JS-0113 return true } - throw new Error('Invalid port provided. Must be a Well-known (0-1023), or Registered (1024-49151), or Private (49152-65535) port number') + return 'Invalid port provided. Must be a Well-known (0-1023), or Registered (1024-49151), or Private (49152-65535) port number' }, }), EMASSER_REQUEST_CERT: await select({ - message: PROMPT_MESSAGE[7], + message: PROMPT_MESSAGE[8], default: true, choices: [ {name: 'true', value: true}, @@ -152,7 +264,7 @@ async function processPrompt() { ], }), EMASSER_REJECT_UNAUTHORIZED: await select({ - message: PROMPT_MESSAGE[8], + message: PROMPT_MESSAGE[9], default: true, choices: [ {name: 'true', value: true}, @@ -160,7 +272,7 @@ async function processPrompt() { ], }), EMASSER_DEBUGGING: await select({ - message: PROMPT_MESSAGE[9], + message: PROMPT_MESSAGE[10], default: false, choices: [ {name: 'true', value: true}, @@ -168,7 +280,7 @@ async function processPrompt() { ], }), EMASSER_CLI_DISPLAY_NULL: await select({ - message: PROMPT_MESSAGE[10], + message: PROMPT_MESSAGE[11], default: false, choices: [ {name: 'true', value: true}, @@ -176,53 +288,122 @@ async function processPrompt() { ], }), EMASSER_EPOCH_TO_DATETIME: await select({ - message: PROMPT_MESSAGE[11], + message: PROMPT_MESSAGE[12], default: true, choices: [ {name: 'true', value: true}, {name: 'false', value: false}, ], }), + EMASSER_DOWNLOAD_DIR: await input({ + message: PROMPT_MESSAGE[13], + default: envConfig.EMASSER_DOWNLOAD_DIR, + }), } - // Collect all of the provided answers - let data = '' + // Add optional content to the collection // eslint-disable-next-line guard-for-in - for (const tagName in answers) { - const answerValue = _.get(answers, tagName) + for (const tagName in optionalContent) { + const answerValue = _.get(optionalContent, tagName) if (answerValue !== null) { - // eslint-disable-next-line unicorn/prefer-number-properties - data = isNaN(answerValue) ? data + tagName + "='" + answerValue + "'\n" : data + tagName + '=' + answerValue + '\n' + interactiveValues[tagName] = answerValue } } - // Write the .env file - let envGenerated = true - try { - fse.writeFileSync('.env', data) - } catch { - envGenerated = false - } + // Save content to the .env file + updateKeyValuePairs('.env', interactiveValues) // Output the content of the new or updated .env configuration file - if (envGenerated) { - const table: string[][] = fse.readFileSync('.env', 'utf8').split('\n').map(pair => pair.split('=')) - const envData: object = Object.fromEntries(table) + const table: string[][] = fse.readFileSync('.env', 'utf8').split('\n').map(pair => pair.split('=')) + const envData: object = Object.fromEntries(table) - console.log('\n', colors.yellow('An eMASS configuration file with the following environment variables was created:')) + console.log('\n', colors.yellow('An eMASS configuration file with the following environment variables was created:')) - for (const [key, value] of Object.entries(envData)) { - if (key.trim() !== '') { - console.log(`\t${colors.blue(key)}=${colors.green(value)}`) - } + for (const [key, value] of Object.entries(envData)) { + // if ((key.trim() !== '' || !key.startsWith('#'))) { + if (key.startsWith('#')) { + console.log(`\t${colors.green(key)}`) + } else if (key.trim() !== '') { + console.log(`\t${colors.blue(key)}=${colors.dim(value)}`) } + } + + console.log('\n', colors.yellow('To modify any of the entries, simple run the configure command again.')) + console.log('\n', colors.cyan.bold('To verify connection to the eMASS services use the command: '), colors.green.underline('saf emasser get test_connection')) + console.log('\n', colors.cyan.bold('For additional help on available eMASS CLI API commands use: '), colors.green.underline('saf emasser -h or -help')) +} + +/** + * Updates key-value pairs in a file based on the provided updates object. + * + * @param filePath - The path to the file to be updated. + * @param updates - An object containing key-value pairs to update in the file. + * The keys represent the keys in the file, and the values represent the new values to be set. + * If the value is a string, it will be wrapped in single quotes. + * + * @throws Will throw an error if there is an issue reading or writing the file. + * + */ +function updateKeyValuePairs(filePath: fse.PathOrFileDescriptor, updates: { [x: string]: any; hasOwnProperty: (arg0: string) => any }) { + try { + // Read the file content + const fileContent = fse.readFileSync(filePath, 'utf8') - console.log('\n', colors.yellow('To modify any of the entries, simple run the configure command again.')) - console.log('\n', colors.cyan.bold('To verify connection to the eMASS services use the command: '), colors.green.underline('saf emasser get test_connection')) - console.log('\n', colors.cyan.bold('For additional help on available eMASS CLI API commands use: '), colors.green.underline('saf emasser -h or -help')) + // Split the content into lines + const lines = fileContent.split('\n') + + // Iterate over each line to find and update key-value pairs + const updatedLines = lines.map(line => { + // Trim the line to remove any leading/trailing whitespace + const trimmedLine = line.trim() + + // Check if the line contains a key-value pair (e.g., key=value) + const [key, value] = trimmedLine.split('=') // skipcq: JS-0356 + + // If the key exists in the updates object, update the value + if (Object.prototype.hasOwnProperty.call(updates, key)) { + // wrap string values with single-quotes + return isNumeric(updates[key]) ? `${key}=${updates[key]}` : ((typeof updates[key] === 'string') ? `${key}='${updates[key]}'` : `${key}=${updates[key]}`) + } + + // Return the original line if no update is needed + return line + }) + + // Join the updated lines back into a single string + const updatedContent = updatedLines.join('\n') + + // Write the updated content back to the file + fse.writeFileSync(filePath, updatedContent, 'utf8') + + console.log('File updated successfully.') + } catch (error) { + console.error('Error reading or writing file:', error) + process.exit(1) } } +/** + * Checks if the given value is numeric. + * + * This function tests whether the provided value is a number or a string that represents a number. + * It supports both integer and floating-point numbers, including negative values. + * + * @param value - The value to be checked. It can be a string or a number. + * @returns `true` if the value is numeric, otherwise `false`. + */ +function isNumeric(value: string | number) { + return /^-?\d+(\.\d+)?$/.test(value.toString()) +} + +/** + * Generates or updates a configuration file based on the presence of an existing `.env` file. + * + * If a `.env` file already exists, prompts the user to update the existing values or accept the current ones. + * If no `.env` file is found, creates a new configuration file and prompts the user to provide the environment variable values. + * + * @returns {Promise} A promise that resolves when the configuration process is complete. + */ export async function generateConfig() { if (fse.existsSync('.env')) { console.log(colors.yellow('A configuration file already exists, updating - Press Enter to accept the current value(s), otherwise provide new value')) diff --git a/src/utils/emasser/initConnection.ts b/src/utils/emasser/initConnection.ts index 6e6960608..b9321c529 100644 --- a/src/utils/emasser/initConnection.ts +++ b/src/utils/emasser/initConnection.ts @@ -3,8 +3,39 @@ import https from 'https' import FormData from 'form-data' import {ApiConfig} from './apiConfig' import {Configuration} from '@mitre/emass_client/dist/configuration' -import globalAxios, {AxiosInstance, AxiosRequestConfig} from '@mitre/emass_client/node_modules/axios' +import globalAxios, {AxiosInstance, AxiosRequestConfig} from 'axios' +/** + * Initializes and configures Axios instances for making HTTP requests. + * + * @class InitConnections + * + * @property {AxiosRequestConfig} axiosRequestConfig - The Axios request configuration. + * @property {Configuration} configuration - The configuration for the API. + * @property {AxiosInstance} axiosInstances - The Axios instance used for making HTTP requests. + * + * @constructor + * @param {ApiConfig} conf - The API configuration object. + * + * @example + * const apiConfig: ApiConfig = { + * url: 'https://api.example.com', + * reqCert: true, + * sslVerify: true, + * keyCert: '/path/to/keyCert', + * clientCert: '/path/to/clientCert', + * apiPassPhrase: 'yourPassPhrase', + * port: 443, + * apiKey: 'yourApiKey', + * userUid: 'yourUserUid', + * caCert: '/path/to/caCert' + * }; + * const initConnections = new InitConnections(apiConfig); + * + * @remarks + * This class handles the creation of an Axios instance with custom HTTPS agent settings. + * It supports client certificate authentication and allows configuring SSL verification. + */ export class InitConnections { private axiosRequestConfig: AxiosRequestConfig; public configuration: Configuration; @@ -25,13 +56,23 @@ export class InitConnections { // and Infinity, in which case Connection: close will be used. Default: false. // requestCert true to specify whether a server should request a certificate from a connecting client. Only applies when isServer is true. // rejectUnauthorized If not false a server automatically reject clients with invalid certificates. Only applies when isServer is true. - this.axiosRequestConfig = { + + this.axiosRequestConfig = conf.caCert === undefined ? { + httpsAgent: new https.Agent({ + keepAlive: true, + requestCert: conf.reqCert, + rejectUnauthorized: conf.sslVerify, + key: conf.keyCert ? fs.readFileSync(conf.keyCert) : undefined, + cert: conf.clientCert ? fs.readFileSync(conf.clientCert) : undefined, + passphrase: conf.apiPassPhrase, + port: conf.port, + }), + } : { httpsAgent: new https.Agent({ keepAlive: true, requestCert: conf.reqCert, rejectUnauthorized: conf.sslVerify, - key: fs.readFileSync(conf.keyCert), - cert: fs.readFileSync(conf.clientCert), + ca: fs.readFileSync(conf.caCert), passphrase: conf.apiPassPhrase, port: conf.port, }), diff --git a/src/utils/emasser/outputError.ts b/src/utils/emasser/outputError.ts index 0c4809c08..cf536b615 100644 --- a/src/utils/emasser/outputError.ts +++ b/src/utils/emasser/outputError.ts @@ -1,11 +1,27 @@ import _ from 'lodash' +/** + * Generates a formatted error message from the provided data object. + * + * @param data - The data object containing error information. + * @returns A stringified JSON object representing the error message. + * + * The function checks for the presence of specific properties within the data object: + * - If `data.status` exists, it uses this value as the error code. + * - If `data.code` exists, it uses this value as the error message. + * - If `data.response.data` exists, it uses this object as the result. + * - If `data.response.status` exists, it uses this value as the error + * code and `data.response.statusText` as the error message. + * + * If none of the above properties are found, it defaults to an error code of + * 'Unknown' and an error message of 'Unable to access error message(s)'. + */ export function outputError(data: object): string { - let result: object = {meta: {code: 'Unknown', errorMessage: 'Unable to access error message(s)'}} + let result: object = {meta: {code: (_.has(data, 'status')) ? _.get(data, 'status') : 'Unknown', + errorMessage: (_.has(data, 'code')) ? _.get(data, 'code') : 'Unable to access error message(s)'}} if (_.has(data, 'response.data')) { - // Need to use the non null assertion (!) or return and empty objet as _.get can return undefined - result = _.get(data, 'response.data') || {} + result = _.get(data, 'response.data') || result } else if (_.has(data, 'response.status')) { result = {meta: {code: _.get(data, 'response.status'), errorMessage: _.get(data, 'response.statusText')}} } diff --git a/src/utils/emasser/outputFormatter.ts b/src/utils/emasser/outputFormatter.ts index 447f33556..de5f86116 100644 --- a/src/utils/emasser/outputFormatter.ts +++ b/src/utils/emasser/outputFormatter.ts @@ -1,6 +1,12 @@ import {ApiConfig} from './apiConfig' import _ from 'lodash' +/** + * Removes all properties with null values from the given object. + * + * @param dataObject - The object from which null properties should be removed. + * @returns A new object with all null properties removed. + */ function removeNullsFromObject(dataObject: object): object { const jsonData: {[key: string]: any} = {}; @@ -13,6 +19,16 @@ function removeNullsFromObject(dataObject: object): object { return jsonData } +/** + * Converts epoch timestamps to Date objects in a given data object. + * + * This function recursively processes an object, converting any epoch timestamps + * (values that are numeric and likely represent a date) to JavaScript Date objects. + * It handles nested objects and arrays, ensuring all applicable timestamps are converted. + * + * @param dataObject - The input object containing potential epoch timestamps. + * @returns A new object with epoch timestamps converted to Date objects. + */ function convertEpochToDateTime(dataObject: object): object { const jsonData: {[key: string]: any} = {}; (Object.keys(dataObject) as (keyof typeof dataObject)[]).forEach(key => { @@ -45,6 +61,23 @@ function convertEpochToDateTime(dataObject: object): object { return jsonData } +/** + * Formats the given data object based on the configuration settings. + * + * @param data - The data object to be formatted. + * @param doConversion - A boolean flag indicating whether to perform data conversion. Defaults to true. + * @returns The formatted data as a JSON string. + * + * The function performs the following operations based on the configuration: + * - If debugging is enabled, it logs the entire data object. + * - If the data object contains headers, it extracts the data property. + * - If doConversion is true, it processes the data object to remove null values and convert epoch times to human-readable dates. + * - If hideNulls is true, it removes null values from the data object. + * - If showEpoch is true, it converts epoch times to human-readable dates. + * + * The function handles different structures of the data object, including arrays and nested objects. + * It merges the processed data into a new object and returns it as a formatted JSON string. + */ export function outputFormat(data: object, doConversion = true): string { const conf = new ApiConfig() const hideNulls: boolean = conf.displayNulls !== 'true' @@ -56,13 +89,14 @@ export function outputFormat(data: object, doConversion = true): string { // When debugging is on, output the entire content returned from // the server. Output text in yellow and revert back to default console.log('\x1B[93m', 'Debugging is on', '\x1B[0m') + console.log('\x1B[96m', 'Start Debug =================================================================', '\x1B[0m') try { console.log(JSON.stringify(data, null, 2)) } catch { console.log(data) } - return '' + console.log('\x1B[96m', 'End Debug ===============================================================', '\x1B[0m') } try { diff --git a/src/utils/emasser/utilities.ts b/src/utils/emasser/utilities.ts index a4976bc7b..dbbe8d2a6 100644 --- a/src/utils/emasser/utilities.ts +++ b/src/utils/emasser/utilities.ts @@ -1,13 +1,28 @@ +/* eslint-disable valid-jsdoc */ import _ from 'lodash' import {Flags} from '@oclif/core' import {BooleanFlag, OptionFlag} from '@oclif/core/interfaces' - +import fs from 'fs' +import path from 'path' + +/** + * Interface representing the command line arguments. + * + * @property {string} requestType - The type of request to be made. + * @property {string} endpoint - The endpoint to which the request is directed. + * @property {string} argument - Additional argument for the request. + */ interface CliArgs { requestType: string; endpoint: string; argument: string; } +/** + * Interface representing various flag options used in the application. + * Property are listed here as optional but are set to required based + * on what endpoint being supported (see getFlagsForEndpoint) + */ export interface FlagOptions { systemId?: OptionFlag; poamId?: OptionFlag; @@ -21,47 +36,49 @@ export interface FlagOptions { excludeInherited?: BooleanFlag; includeInactive?: BooleanFlag; isTemplate?: BooleanFlag; - includePackage?: BooleanFlag; includeDitprMetrics?: BooleanFlag; includeDecommissioned?: BooleanFlag; reportsForScorecard?: BooleanFlag; - acronyms?: BooleanFlag; latestOnly?: BooleanFlag; systemOnly?: BooleanFlag; compress?: BooleanFlag; + printToStdOut?: BooleanFlag; policy?: OptionFlag; registrationType?: OptionFlag; ditprId?: OptionFlag; coamsId?: OptionFlag; roleCategory?: OptionFlag; role?: OptionFlag; + acronyms?: OptionFlag; controlAcronyms?: OptionFlag; + assessmentProcedures?: OptionFlag; ccis?: OptionFlag; sinceDate?: OptionFlag; scheduledCompletionDateStart?: OptionFlag; scheduledCompletionDateEnd?: OptionFlag; filename?: OptionFlag; status?: OptionFlag; - cci?: OptionFlag; + assessmentProcedure?: OptionFlag; testedBy?: OptionFlag; testDate?: OptionFlag; description?: OptionFlag; + artifactDescription?: OptionFlag; complianceStatus?: OptionFlag; scheduledCompletionDate?: OptionFlag; orgId?:OptionFlag; pageSize?: OptionFlag; - input?: OptionFlag; fileName?: OptionFlag; - poamFile?: OptionFlag; - controlFile?: OptionFlag; - cloudResourceFile?: OptionFlag; - statiCodeScanFile?: OptionFlag; - containerCodeScanFile?: OptionFlag; + resourceId?: OptionFlag; + containerId?: OptionFlag; + assetsHardwareId?: OptionFlag; + assetsSoftwareId?: OptionFlag; + dataFile?: OptionFlag; type?: OptionFlag; category?: OptionFlag; refPageNumber?: OptionFlag; controls?: OptionFlag; - artifactExpirationDate?: OptionFlag; + signedDate?: OptionFlag; + expirationDate?: OptionFlag; lastReviewDate?: OptionFlag; controlAcronym?: OptionFlag; comments?: OptionFlag; @@ -69,7 +86,13 @@ export interface FlagOptions { name?: OptionFlag; } -// Supporting Function +/** + * Parses command line arguments to extract the request type, endpoint, and additional argument. + * + * @param argv - An array of command line arguments. + * @param endpointValue - An optional endpoint value to override the one in the command line arguments. + * @returns An object containing the request type, endpoint, and additional argument. + */ function getArgs(argv: string[], endpointValue?: string): CliArgs { const requestTypeIndex = argv.findIndex(arg => (arg === 'get' || arg === 'post' || arg === 'put' || arg === 'delete')) return { @@ -79,6 +102,24 @@ function getArgs(argv: string[], endpointValue?: string): CliArgs { } } +/** + * Generates flag options for a given endpoint based on the provided command line arguments. + * + * @param argv - The command line arguments. + * @returns An object containing the flag options for the specified endpoint. + * + * The function processes different request types (`get`, `post`, `put`, `delete`) and endpoints + * to generate the appropriate flag options. Each endpoint has its own set of flags with specific + * descriptions and requirements. + * + * Example usage: + * ```typescript + * const flags = getFlagsForEndpoint(['get', 'system']); + * ``` + * + * The returned `FlagOptions` object will vary based on the endpoint and request type. + */ +// skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS-0044 const args: CliArgs = getArgs(argv) let flagObj: FlagOptions = {} @@ -89,7 +130,6 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS case 'system': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - includePackage: Flags.boolean({char: 'I', description: 'Boolean - include system packages', allowNo: true, required: false}), policy: Flags.string({char: 'p', description: 'Filter on policy', required: false, options: ['diacap', 'rmf', 'reporting']}), } break @@ -102,7 +142,6 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS ditprId: Flags.string({char: 't', description: 'DoD Information Technology (IT) Portfolio Repository (DITPR) string Id', required: false}), coamsId: Flags.string({char: 'c', description: 'Cyber Operational Attributes Management System (COAMS) string Id', required: false}), policy: Flags.string({char: 'p', description: 'Filter on policy', options: ['diacap', 'rmf', 'reporting'], required: false}), - includePackage: Flags.boolean({char: 'I', description: 'Boolean - include system packages', allowNo: true, required: false}), includeDitprMetrics: Flags.boolean({char: 'M', description: 'Boolean - include DoD Information Technology metrics', allowNo: true, required: false}), includeDecommissioned: Flags.boolean({char: 'D', description: 'Boolean - include decommissioned systems', allowNo: true, required: false}), reportsForScorecard: Flags.boolean({char: 'S', description: 'Boolean - include score card', allowNo: true, required: false}), @@ -114,10 +153,8 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS if (args.argument === 'byCategory') { flagObj = { roleCategory: Flags.string({char: 'c', description: 'Filter on role category', options: ['CAC', 'PAC', 'Other'], required: true}), - role: Flags.string({char: 'r', description: 'Filter on role type', - options: ['AO', 'Auditor', 'Artifact Manager', 'C&A Team', 'IAO', 'ISSO', 'PM/IAM', 'SCA', 'User Rep', 'Validator'], required: true}), + role: Flags.string({char: 'r', description: 'Accepts single value from options available at base system-roles endpoint e.g., SCA', required: true}), policy: Flags.string({char: 'p', description: 'Filter on policy', options: ['diacap', 'rmf', 'reporting'], required: false}), - includeDecommissioned: Flags.boolean({char: 'D', description: 'Boolean - include decommissioned systems', allowNo: true, required: false}), } } @@ -127,29 +164,7 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS case 'controls': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - acronyms: Flags.boolean({char: 'A', description: 'The system acronym(s) e.g "AC-1, AC-2" - if not provided all controls for systemId are returned', allowNo: true, required: false}), - } - break - } - - case 'cac': { - flagObj = { - systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - controlAcronyms: Flags.string({char: 'a', description: 'The system acronym(s) e.g "AC-1, AC-2"', required: false}), - } - break - } - - case 'pac': { - flagObj = { - systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - } - break - } - - case 'cmmc': { - flagObj = { - sinceDate: Flags.string({char: 'd', description: 'The CMMC date. Unix date format', required: true}), + acronyms: Flags.string({char: 'A', description: 'The system acronym(s) e.g "AC-1, AC-2" - if not provided all controls for systemId are returned', required: false}), } break } @@ -158,28 +173,21 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), controlAcronyms: Flags.string({char: 'a', description: 'The system acronym(s) e.g "AC-1, AC-2"', required: false}), + assessmentProcedures: Flags.string({char: 'p', description: 'The system Security Control Assessment Procedure e.g "AC-1.1,AC-1.2', required: false}), ccis: Flags.string({char: 'c', description: 'The system CCIS string numerical value', required: false}), latestOnly: Flags.boolean({char: 'L', description: 'Boolean - Filter on latest only', allowNo: true, required: false}), } break } - case 'workflow_definitions': { - flagObj = { - includeInactive: Flags.boolean({char: 'i', description: 'Boolean - Include inactive workflows', allowNo: true, required: false}), - registrationType: Flags.string({char: 'r', description: 'The registration type - must be a valid type', - options: ['assessAndAuthorize', 'assessOnly', 'guest', 'regular', 'functional', 'cloudServiceProvider', 'commonControlProvider'], required: false}), - } - break - } - case 'poams': { if (args.argument === 'forSystem') { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - scheduledCompletionDateStart: Flags.string({description: 'The completion start date', required: false}), - scheduledCompletionDateEnd: Flags.string({description: 'The completion end date', required: false}), + scheduledCompletionDateStart: Flags.string({char: 'd', description: 'The scheduled completion start date', required: false}), + scheduledCompletionDateEnd: Flags.string({char: 'e', description: 'The scheduled completion end date', required: false}), controlAcronyms: Flags.string({char: 'a', description: 'The system acronym(s) e.g "AC-1, AC-2"', required: false}), + assessmentProcedures: Flags.string({char: 'p', description: 'The system Security Control Assessment Procedure e.g "AC-1.1,AC-1.2', required: false}), ccis: Flags.string({char: 'c', description: 'The system CCIS string numerical value', required: false}), systemOnly: Flags.boolean({char: 'Y', description: 'Boolean - Return only systems', allowNo: true, required: false}), } @@ -193,12 +201,32 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS break } + case 'milestones': { + if (args.argument === 'byPoamId') { + flagObj = { + systemId: Flags.integer({char: 's', description: 'Unique system identifier', required: true}), + poamId: Flags.integer({char: 'p', description: 'Unique poam identifier', required: true}), + scheduledCompletionDateStart: Flags.string({char: 't', description: 'Unix time format (e.g. 1499644800)', required: false}), + scheduledCompletionDateEnd: Flags.string({char: 'c', description: 'Unix time format (e.g. 1499990400)', required: false}), + } + } else if (args.argument === 'byMilestoneId') { + flagObj = { + systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), + poamId: Flags.integer({char: 'p', description: 'The POAM identification number', required: true}), + milestoneId: Flags.integer({char: 'm', description: 'Unique milestone identifier', required: true}), + } + } + + break + } + case 'artifacts': { if (args.argument === 'forSystem') { flagObj = { systemId: Flags.integer({char: 's', description: 'Unique system identifier', required: true}), filename: Flags.string({char: 'f', description: 'The artifact file name', required: false}), controlAcronyms: Flags.string({char: 'a', description: 'The system acronym(s) e.g "AC-1, AC-2"', required: false}), + assessmentProcedures: Flags.string({char: 'p', description: 'The system Security Control Assessment Procedure e.g "AC-1.1,AC-1.2', required: false}), ccis: Flags.string({char: 'c', description: 'The system CCIS string numerical value', required: false}), systemOnly: Flags.boolean({char: 'y', description: 'Boolean - Return only systems', allowNo: true, required: false}), } @@ -207,28 +235,44 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), filename: Flags.string({char: 'f', description: 'The artifact file name', required: true}), compress: Flags.boolean({char: 'C', description: 'Boolean - Compress true or false', allowNo: true, required: false}), + printToStdOut: Flags.boolean({char: 'P', description: 'Boolean - Print to standard output', allowNo: true, required: false}), } } break } - case 'milestones': { - if (args.argument === 'byPoamId') { - flagObj = { - systemId: Flags.integer({char: 's', description: 'Unique system identifier', required: true}), - poamId: Flags.integer({char: 'p', description: 'Unique poam identifier', required: true}), - scheduledCompletionDateStart: Flags.string({char: 't', description: 'Unix time format (e.g. 1499644800)', required: false}), - scheduledCompletionDateEnd: Flags.string({char: 'c', description: 'Unix time format (e.g. 1499990400)', required: false}), - } - } else if (args.argument === 'byMilestoneId') { - flagObj = { - systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - poamId: Flags.integer({char: 'p', description: 'The poam identification number', required: true}), - milestoneId: Flags.integer({char: 'm', description: 'Unique milestone identifier', required: true}), - } + case 'cac': { + flagObj = { + systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), + controlAcronyms: Flags.string({char: 'a', description: 'The system acronym(s) e.g "AC-1, AC-2"', required: false}), + } + break + } + + case 'pac': { + flagObj = { + systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), + } + break + } + + case 'hardware': + case 'software': { + flagObj = { + systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), + pageIndex: Flags.integer({char: 'i', description: 'The index of the starting page (default first page 0)', required: false}), + pageSize: Flags.integer({char: 'S', description: 'The number of entries per page (default 20000)', required: false}), } + break + } + case 'workflow_definitions': { + flagObj = { + includeInactive: Flags.boolean({char: 'I', description: 'Boolean - Include inactive workflows', allowNo: true, required: false}), + registrationType: Flags.string({char: 'r', description: 'The registration type - must be a valid type', + options: ['assessAndAuthorize', 'assessOnly', 'guest', 'regular', 'functional', 'cloudServiceProvider', 'commonControlProvider'], required: false}), + } break } @@ -237,7 +281,7 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS flagObj = { includeComments: Flags.boolean({char: 'C', description: 'Boolean - Include transition comments', allowNo: true, required: false}), includeDecommissionSystems: Flags.boolean({char: 'D', description: 'Boolean - Include decommissioned systems', allowNo: true, required: false}), - pageIndex: Flags.integer({char: 'i', description: 'The page number to query', required: false}), + pageIndex: Flags.integer({char: 'i', description: 'The page number to query (default first page 0)', required: false}), sinceDate: Flags.string({char: 'd', description: 'The Workflow Instance date. Unix date format', required: false}), status: Flags.string({char: 's', description: 'The Workflow status - must be a valid status. if not provided includes all systems', options: ['active', 'inactive', 'all'], required: false}), } @@ -250,6 +294,13 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS break } + case 'cmmc': { + flagObj = { + sinceDate: Flags.string({char: 'd', description: 'The CMMC date. Unix date format', required: true}), + } + break + } + case 'dashboards': { flagObj = { orgId: Flags.integer({char: 'o', description: 'The organization identification number', required: true}), @@ -269,11 +320,11 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS case 'test_results': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - cci: Flags.string({char: 'c', description: 'The system CCI string numerical value', required: true}), + assessmentProcedure: Flags.string({char: 'a', description: 'The Security Control Assessment Procedure being assessed', required: true}), testedBy: Flags.string({char: 'b', description: 'The person that conducted the test (Last Name, First)', required: true}), testDate: Flags.string({char: 't', description: 'The date test was conducted, Unix time format', required: true}), description: Flags.string({char: 'd', description: 'The description of test result. 4000 Characters', required: true}), - complianceStatus: Flags.string({char: 'S', description: 'The system CCI string numerical value', + complianceStatus: Flags.string({char: 'S', description: 'The compliance status of the test result', options: ['Compliant', 'Non-Compliant', 'Not Applicable'], required: true}), } break @@ -292,11 +343,10 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS case 'artifacts': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - input: Flags.string({char: 'i', description: 'Artifact file(s) to post to the given system, can have multiple (space separated)', required: true, multiple: true}), - isTemplate: Flags.boolean({char: 'T', description: 'Boolean - Indicates whether an artifact is a template.', allowNo: true, required: false}), - type: Flags.string({char: 't', description: 'Artifact file type', - options: ['Procedure', 'Diagram', 'Policy', 'Labor', 'Document', 'Image', 'Other', 'Scan Result', 'Auditor Report'], required: false}), - category: Flags.string({char: 'c', description: 'Artifact category', options: ['Implementation Guidance', 'Evidence'], required: false}), + fileName: Flags.string({char: 'f', description: 'Artifact file(s) to post to the given system, can have multiple (space separated)', required: true, multiple: true}), + isTemplate: Flags.boolean({char: 'T', description: 'Boolean - Indicates whether an artifact is a template.', allowNo: true, required: false, default: false}), + type: Flags.string({char: 't', description: 'Various artifact file type are accepted (defined by the eMASS administrator)', required: false, default: 'Other'}), + category: Flags.string({char: 'c', description: 'Various artifact category are accepted (defined by the eMASS administrator)', required: false, default: 'Evidence'}), } break } @@ -321,34 +371,44 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS break } - case 'poams': { + case 'poams': + case 'hardware_baseline': + case 'software_baseline': + case 'cloud_resources': + case 'static_code_scans': + case 'container_scans': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - poamFile: Flags.string({char: 'f', description: 'A well formed JSON file with the POA&M(s) to add. It can ba a single object or an array of objects.', required: true}), + dataFile: Flags.string({char: 'f', description: 'A well formed JSON file containing the data to add. It can ba a single object or an array of objects.', required: true}), } break } + } - case 'cloud_resources': { - flagObj = { - systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - cloudResourceFile: Flags.string({char: 'f', description: 'A well formed JSON file with the cloud resources and their scan results. It can ba a single object or an array of objects.', required: true}), - } - break - } + break + } - case 'static_code_scans': { + case 'put': { + switch (args.endpoint) { // skipcq: JS-0047 + case 'milestones': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - statiCodeScanFile: Flags.string({char: 'f', description: 'A well formed JSON file with application scan findings. It can ba a single object or an array of objects.', required: true}), + poamId: Flags.integer({char: 'p', description: 'The poam identification number', required: true}), + milestoneId: Flags.integer({char: 'm', description: 'Unique milestone identifier', required: true}), + description: Flags.string({char: 'd', description: 'The milestone description', required: true}), + scheduledCompletionDate: Flags.string({char: 'c', description: 'The scheduled completion date - Unix time format', required: false}), } break } - case 'container_scans': { + case 'poams': + case 'controls': + case 'artifacts': + case 'hardware_baseline': + case 'software_baseline': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - containerCodeScanFile: Flags.string({char: 'f', description: 'A well formed JSON file with container scan results. It can ba a single object or an array of objects.', required: true}), + dataFile: Flags.string({char: 'f', description: 'A well formed JSON file containing the data to be updated. It can ba a single object or an array of objects.', required: true}), } break } @@ -357,22 +417,12 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS break } - case 'put': { + case 'delete': { switch (args.endpoint) { // skipcq: JS-0047 case 'artifacts': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - filename: Flags.string({char: 'f', description: 'Artifact file name to update for the given system', required: true}), - isTemplate: Flags.boolean({char: 'T', description: 'Boolean - Indicates whether an artifact is a template.', allowNo: true, required: true}), - type: Flags.string({char: 't', description: 'Artifact file type', - options: ['Procedure', 'Diagram', 'Policy', 'Labor', 'Document', 'Image', 'Other', 'Scan Result', 'Auditor Report'], required: true}), - category: Flags.string({char: 'g', description: 'Artifact category', options: ['Implementation Guidance', 'Evidence'], required: true}), - description: Flags.string({char: 'd', description: 'The artifact(s) description', required: false}), - refPageNumber: Flags.string({char: 'p', description: 'Artifact reference page number', required: false}), - ccis: Flags.string({char: 'c', description: 'CCIs associated with artifact', required: false}), - controls: Flags.string({char: 'C', description: 'Control acronym associated with the artifact. NIST SP 800-53 Revision 4 defined.', required: false}), - artifactExpirationDate: Flags.string({char: 'D', description: 'Date artifact expires and requires review', required: false}), - lastReviewDate: Flags.string({char: 'R', description: 'Date artifact was last reviewed', required: false}), + fileName: Flags.string({char: 'f', description: 'The artifact file name to remove, can have multiple (space separated)', required: true, multiple: true}), } break } @@ -381,9 +431,7 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), poamId: Flags.integer({char: 'p', description: 'The poam identification number', required: true}), - milestoneId: Flags.integer({char: 'm', description: 'Unique milestone identifier', required: true}), - description: Flags.string({char: 'd', description: 'The milestone description', required: false}), - scheduledCompletionDate: Flags.string({char: 'c', description: 'The scheduled completion date - Unix time format', required: false}), + milestonesId: Flags.integer({char: 'm', description: 'Unique milestone identifier, can have multiple (space separated)', required: true, multiple: true}), } break } @@ -391,46 +439,39 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS case 'poams': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - poamFile: Flags.string({char: 'f', description: 'A well formed JSON file with the POA&M(s) to updated the specified system. It can ba a single object or an array of objects.', required: true}), + poamsId: Flags.integer({char: 'p', description: 'Unique POA&M identification number, can have multiple (space separated)', required: true, multiple: true}), } break } - case 'controls': { + case 'hardware_baseline': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - controlFile: Flags.string({char: 'f', description: 'A well formed JSON file with the Security Control information to updated the specified system. It can ba a single object or an array of objects.', required: true}), + assetsHardwareId: Flags.string({char: 'a', description: 'Unique GUID identifying a specific hardware asset, can have multiple (space separated)', required: true, multiple: true}), } break } - } - break - } - - case 'delete': { - switch (args.endpoint) { // skipcq: JS-0047 - case 'artifacts': { + case 'software_baseline': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - fileName: Flags.string({char: 'F', description: 'The artifact file name to remove, can have multiple (space separated)', required: true, multiple: true}), + assetsSoftwareId: Flags.string({char: 'a', description: 'Unique GUID identifying a specific software asset, can have multiple (space separated)', required: true, multiple: true}), } break } - case 'milestones': { + case 'cloud_resources': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - poamId: Flags.integer({char: 'p', description: 'The poam identification number', required: true}), - milestonesId: Flags.integer({char: 'M', description: 'Unique milestone identifier, can have multiple (space separated)', required: true, multiple: true}), + resourceId: Flags.string({char: 'r', description: 'Unique identifier/resource namespace for policy compliance result, can have multiple (space separated)', required: true, multiple: true}), } break } - case 'poams': { + case 'container_scans': { flagObj = { systemId: Flags.integer({char: 's', description: 'The system identification number', required: true}), - poamsId: Flags.integer({char: 'P', description: 'Unique POA&M identification number, can have multiple (space separated)', required: true, multiple: true}), + containerId: Flags.string({char: 'c', description: 'Unique identifier of the container, can have multiple (space separated)', required: true, multiple: true}), } break } @@ -443,6 +484,14 @@ export function getFlagsForEndpoint(argv: string[]): FlagOptions { // skipcq: JS return flagObj } +/** + * Retrieves a description for a given endpoint based on the provided arguments. + * + * @param argv - The command line arguments passed to the function. + * @param endpoint - The endpoint for which the description is to be retrieved. + * @returns A string description for the specified endpoint and arguments. + */ +// skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold export function getDescriptionForEndpoint(argv: string[], endpoint: string): string { // skipcq: JS-0044 const args: CliArgs = getArgs(argv, endpoint) let description = '' @@ -499,7 +548,7 @@ export function getDescriptionForEndpoint(argv: string[], endpoint: string): str } case 'export': { - description = 'Retrieves the artifact file (if compress is true the file binary contents are returned, otherwise the file textual contents are returned.)' + description = 'Retrieves an artifact file for selected system\n(file is sent to EMASSER_DOWNLOAD_DIR (defaults to eMASSerDownloads) if flag [-P, --printToStdOut] not provided)' break } @@ -561,18 +610,68 @@ export function getDescriptionForEndpoint(argv: string[], endpoint: string): str break } + case 'terms_conditions_summary': { + description = 'Get systems terms/conditions summary dashboard information' + break + } + + case 'terms_conditions_details': { + description = 'Get systems terms/conditions details dashboard information' + break + } + + case 'connectivity_ccsd_summary': { + description = 'Get systems connectivity/CCSD summary dashboard information' + break + } + + case 'connectivity_ccsd_details': { + description = 'Get systems connectivity/CCSD details dashboard information' + break + } + + case 'atc_iatc_details': { + description = 'Get systems ATC/IATC details dashboard information' + break + } + + case 'questionnaire_summary': { + description = 'Get systems questionnaire summary dashboard information' + break + } + + case 'questionnaire_details': { + description = 'Get systems questionnaire details dashboard information' + break + } + + case 'workflows_history_summary': { + description = 'Get systems workflow history summary dashboard information' + break + } + + case 'workflows_history_details': { + description = 'Get systems workflow history details dashboard information' + break + } + + case 'workflows_history_stage_details': { + description = 'Get systems workflow history stage details dashboard information' + break + } + case 'control_compliance_summary': { - description = 'Get enterprise systems control compliance summary dashboard information' + description = 'Get systems control compliance summary dashboard information' break } case 'security_control_details': { - description = 'Get enterprise systems security control details dashboard information' + description = 'Get systems security control details dashboard information' break } case 'assessment_procedures_details': { - description = 'Get enterprise systems assessment procedures details dashboard information' + description = 'Get systems assessment procedures details dashboard information' break } @@ -616,6 +715,61 @@ export function getDescriptionForEndpoint(argv: string[], endpoint: string): str break } + case 'software_summary': { + description = 'Get system software baseline summary dashboard information' + break + } + + case 'software_details': { + description = 'Get systems software baseline details dashboard information' + break + } + + case 'sensor_software_summary': { + description = 'Get system sensor-based software summary dashboard information' + break + } + + case 'sensor_software_details': { + description = 'Get system sensor-based software details dashboard information' + break + } + + case 'sensor_software_counts': { + description = 'Get system sensor-based software counts dashboard information' + break + } + + case 'critical_assets_summary': { + description = 'Get system critical assets summary dashboard information' + break + } + + case 'vulnerability_summary': { + description = 'Get system vulnerability summary dashboard information' + break + } + + case 'device_findings_summary': { + description = 'Get system device findings summary dashboard information' + break + } + + case 'device_findings_details': { + description = 'Get system device findings details dashboard information' + break + } + + case 'application_findings_summary': { + description = 'Get system application findings summary dashboard information' + break + } + + case 'application_findings_details': { + description = 'Get system application findings details dashboard information' + break + } + case 'ports_protocols_summary': { description = 'Get system ports and protocols summary dashboard information' break @@ -626,16 +780,41 @@ export function getDescriptionForEndpoint(argv: string[], endpoint: string): str break } + case 'integration_status_summary': { + description = 'Get system CONMON integration status summary dashboard information' + break + } + case 'associations_details': { description = 'Get system associations details dashboard information' break } - case 'assignments_details': { + case 'user_assignments_details': { description = 'Get user system assignments details dashboard information' break } + case 'org_migration_status': { + description = 'Get organization migration status summary dashboard information' + break + } + + case 'system_migration_status': { + description = 'Get system migration status summary dashboard information' + break + } + + case 'fisma_metrics': { + description = 'Get system FISMA metrics dashboard information' + break + } + + case 'coast_guard_fisma_metrics': { + description = 'Get Coast Guard system FISMA metrics dashboard information' + break + } + case 'privacy_summary': { description = 'Get user system privacy summary dashboard information' break @@ -646,6 +825,11 @@ export function getDescriptionForEndpoint(argv: string[], endpoint: string): str break } + case 'va_icamp_tableau_poam_details': { + description = 'Get VA system ICAMP Tableau POA&M details dashboard information' + break + } + case 'va_aa_summary': { description = 'Get VA system A&A summary dashboard information' break @@ -661,12 +845,12 @@ export function getDescriptionForEndpoint(argv: string[], endpoint: string): str break } - case 'fisma_inventory_summary': { + case 'va_fisma_inventory_summary': { description = 'Get VA system FISMA inventory summary dashboard information' break } - case 'fisma_inventory_crypto_summary': { + case 'va_fisma_inventory_crypto_summary': { description = 'Get VA system FISMA inventory crypto summary dashboard information' break } @@ -686,6 +870,26 @@ export function getDescriptionForEndpoint(argv: string[], endpoint: string): str break } + case 'cmmc_status_summary': { + description = 'Get CMMC assessment status summary dashboard information' + break + } + + case 'cmmc_compliance_summary': { + description = 'Get CMMC assessment requirements compliance summary dashboard information' + break + } + + case 'cmmc_security_requirements_details': { + description = 'Get CMMC assessment security requirements details dashboard information' + break + } + + case 'cmmc_requirement_objectives_details': { + description = 'Get CMMC assessment requirement objectives details dashboard information' + break + } + default: { description = 'Retrieves a pre-defined dashboard by orgId' break @@ -700,6 +904,14 @@ export function getDescriptionForEndpoint(argv: string[], endpoint: string): str return description } +/** + * Generates example command strings for a given endpoint and arguments. + * + * @param argv - The array of command-line arguments. + * @param endpoint - The optional endpoint to generate examples for. + * @returns An array of example command strings. + */ +// skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold export function getExamplesForEndpoint(argv: string[], endpoint?: string): string[] { // skipcq: JS-0044 const args: CliArgs = getArgs(argv, endpoint) // <%= config.bin %> resolves to the executable name @@ -764,7 +976,10 @@ export function getExamplesForEndpoint(argv: string[], endpoint?: string): strin } default: { - exampleArray.push(`${baseCmd} forSystem [-s, --systemId] [options]`, `${baseCmd} export [-s, --systemId] [-f, --filename] [options]`) + exampleArray.push( + `${baseCmd} forSystem [-s, --systemId] [options]`, + `${baseCmd} export [-s, --systemId] [-f, --filename] [options]`, + ) break } } @@ -821,6 +1036,56 @@ export function getExamplesForEndpoint(argv: string[], endpoint?: string): strin break } + case 'terms_conditions_summary': { + exampleArray.push(`${baseCmd} terms_conditions_summary [-o, --orgId] [options]`) + break + } + + case 'terms_conditions_details': { + exampleArray.push(`${baseCmd} terms_conditions_details [-o, --orgId] [options]`) + break + } + + case 'connectivity_ccsd_summary': { + exampleArray.push(`${baseCmd} connectivity_ccsd_summary [-o, --orgId] [options]`) + break + } + + case 'connectivity_ccsd_details': { + exampleArray.push(`${baseCmd} connectivity_ccsd_details [-o, --orgId] [options]`) + break + } + + case 'atc_iatc_details': { + exampleArray.push(`${baseCmd} atc_iatc_details [-o, --orgId] [options]`) + break + } + + case 'questionnaire_summary': { + exampleArray.push(`${baseCmd} questionnaire_summary [-o, --orgId] [options]`) + break + } + + case 'questionnaire_details': { + exampleArray.push(`${baseCmd} questionnaire_details [-o, --orgId] [options]`) + break + } + + case 'workflows_history_summary': { + exampleArray.push(`${baseCmd} workflows_history_summary [-o, --orgId] [options]`) + break + } + + case 'workflows_history_details': { + exampleArray.push(`${baseCmd} workflows_history_details [-o, --orgId] [options]`) + break + } + + case 'workflows_history_stage_details': { + exampleArray.push(`${baseCmd} workflows_history_stage_details [-o, --orgId] [options]`) + break + } + case 'control_compliance_summary': { exampleArray.push(`${baseCmd} control_compliance_summary [-o, --orgId] [options]`) break @@ -876,6 +1141,61 @@ export function getExamplesForEndpoint(argv: string[], endpoint?: string): strin break } + case 'software_summary': { + exampleArray.push(`${baseCmd} software_summary [-o, --orgId] [options]`) + break + } + + case 'software_details': { + exampleArray.push(`${baseCmd} software_details [-o, --orgId] [options]`) + break + } + + case 'sensor_software_summary': { + exampleArray.push(`${baseCmd} sensor_software_summary [-o, --orgId] [options]`) + break + } + + case 'sensor_software_details': { + exampleArray.push(`${baseCmd} sensor_software_details [-o, --orgId] [options]`) + break + } + + case 'sensor_software_counts': { + exampleArray.push(`${baseCmd} sensor_software_counts [-o, --orgId] [options]`) + break + } + + case 'critical_assets_summary': { + exampleArray.push(`${baseCmd} critical_assets_summary [-o, --orgId] [options]`) + break + } + + case 'vulnerability_summary': { + exampleArray.push(`${baseCmd} vulnerability_summary [-o, --orgId] [options]`) + break + } + + case 'device_findings_summary': { + exampleArray.push(`${baseCmd} device_findings_summary [-o, --orgId] [options]`) + break + } + + case 'device_findings_details': { + exampleArray.push(`${baseCmd} device_findings_details [-o, --orgId] [options]`) + break + } + + case 'application_findings_summary': { + exampleArray.push(`${baseCmd} application_findings_summary [-o, --orgId] [options]`) + break + } + + case 'application_findings_details': { + exampleArray.push(`${baseCmd} application_findings_details [-o, --orgId] [options]`) + break + } + case 'ports_protocols_summary': { exampleArray.push(`${baseCmd} ports_protocols_summary [-o, --orgId] [options]`) break @@ -886,13 +1206,38 @@ export function getExamplesForEndpoint(argv: string[], endpoint?: string): strin break } + case 'integration_status_summary': { + exampleArray.push(`${baseCmd} integration_status_summary [-o, --orgId] [options]`) + break + } + case 'associations_details': { exampleArray.push(`${baseCmd} associations_details [-o, --orgId] [options]`) break } - case 'assignments_details': { - exampleArray.push(`${baseCmd} assignments_details [-o, --orgId] [options]`) + case 'user_assignments_details': { + exampleArray.push(`${baseCmd} user_assignments_details [-o, --orgId] [options]`) + break + } + + case 'org_migration_status': { + exampleArray.push(`${baseCmd} org_migration_status [-o, --orgId] [options]`) + break + } + + case 'system_migration_status': { + exampleArray.push(`${baseCmd} system_migration_status [-o, --orgId] [options]`) + break + } + + case 'fisma_metrics': { + exampleArray.push(`${baseCmd} fisma_metrics [-o, --orgId] [options]`) + break + } + + case 'coast_guard_fisma_metrics': { + exampleArray.push(`${baseCmd} coast_guard_fisma_metrics [-o, --orgId] [options]`) break } @@ -906,6 +1251,11 @@ export function getExamplesForEndpoint(argv: string[], endpoint?: string): strin break } + case 'va_icamp_tableau_poam_details': { + exampleArray.push(`${baseCmd} va_icamp_tableau_poam_details [-o, --orgId] [options]`) + break + } + case 'va_aa_summary': { exampleArray.push(`${baseCmd} va_aa_summary [-o, --orgId] [options]`) break @@ -921,13 +1271,13 @@ export function getExamplesForEndpoint(argv: string[], endpoint?: string): strin break } - case 'fisma_inventory_summary': { - exampleArray.push(`${baseCmd} fisma_inventory_summary [-o, --orgId] [options]`) + case 'va_fisma_inventory_summary': { + exampleArray.push(`${baseCmd} va_fisma_inventory_summary [-o, --orgId] [options]`) break } - case 'fisma_inventory_crypto_summary': { - exampleArray.push(`${baseCmd} fisma_inventory_crypto_summary [-o, --orgId] [options]`) + case 'va_fisma_inventory_crypto_summary': { + exampleArray.push(`${baseCmd} va_fisma_inventory_crypto_summary [-o, --orgId] [options]`) break } @@ -946,8 +1296,28 @@ export function getExamplesForEndpoint(argv: string[], endpoint?: string): strin break } + case 'cmmc_status_summary': { + exampleArray.push(`${baseCmd} cmmc_status_summary [-o, --orgId] [options]`) + break + } + + case 'cmmc_compliance_summary': { + exampleArray.push(`${baseCmd} cmmc_compliance_summary [-o, --orgId] [options]`) + break + } + + case 'cmmc_security_requirements_details': { + exampleArray.push(`${baseCmd} cmmc_security_requirements_details [-o, --orgId] [options]`) + break + } + + case 'cmmc_requirement_objectives_details': { + exampleArray.push(`${baseCmd} cmmc_requirement_objectives_details [-o, --orgId] [options]`) + break + } + default: { - exampleArray.push(`${baseCmd} [dashboard name] [options]`) + exampleArray.push(`${baseCmd} [dashboard name] [-o, --orgId] [options]`) break } } @@ -960,14 +1330,89 @@ export function getExamplesForEndpoint(argv: string[], endpoint?: string): strin return exampleArray } +/** + * Retrieves JSON examples based on the specified endpoint. + * + * @param {string} [endpoint] - The endpoint for which to retrieve JSON examples. + * @returns {string[]} An array of JSON examples corresponding to the specified endpoint. + * + * The following endpoints are supported: + * - 'poams-post-required': Returns JSON example for POA&M post required fields. + * - 'poams-post-put-required-va': Returns JSON example for POA&M post required fields specific to VA. + * - 'poams-post-conditional': Returns JSON example for POA&M post conditional fields. + * - 'poams-put-required': Returns JSON example for POA&M put required fields. + * - 'poams-put-conditional': Returns JSON example for POA&M put conditional fields. + * - 'poams-post-put-optional': Returns JSON example for POA&M post/put optional fields. + * - 'controls-required': Returns JSON example for controls required fields. + * - 'controls-conditional': Returns JSON example for controls conditional fields. + * - 'controls-optional': Returns JSON example for controls optional fields. + * - 'hardware-post-required': Returns JSON example for hardware post required fields. + * - 'hardware-post-put-conditional': Returns JSON example for hardware post/put conditional fields. + * - 'hardware-post-put-optional': Returns JSON example for hardware post/put optional fields. + * - 'software-post-required': Returns JSON example for software post required fields. + * - 'software-post-put-conditional': Returns JSON example for software post/put conditional fields. + * - 'software-post-put-optional': Returns JSON example for software post/put optional fields. + * - 'cloud_resources-required': Returns JSON example for cloud resources required fields. + * - 'cloud_resources-optional': Returns JSON example for cloud resources optional fields. + * - 'scan_findings-application': Returns JSON example for scan findings application fields. + * - 'scan_findings-applicationFindings': Returns JSON example for scan findings application findings fields. + * - 'scan_findings-clearFindings': Returns JSON example for clearing scan findings. + * - 'container_scans-required': Returns JSON example for container scans required fields. + * - 'container_scans-optional': Returns JSON example for container scans optional fields. + */ +// skipcq: JS-R1005 - Ignore Function cyclomatic complexity high threshold export function getJsonExamples(endpoint?: string): string[] { + if (endpoint === 'controls-required') { + const data = '{ ' + + '"acronym": "System acronym, required to match the NIST SP 800-53 Revision 4.",' + + '"responsibleEntities": "Include written description of Responsible Entities that are responsible for the Security Control.",' + + '"controlDesignation": "One of the following: [Common, System-Specific, Hybrid]",' + + '"estimatedCompletionDate": "Estimation completion date - Field is required for Implementation Plan",' + + '"implementationNarrative": "Includes Security Control comments"' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'controls-conditional') { + const data = '{ ' + + '"commonControlProvider": "Indicate the type of Common Control Provider for an “Inherited” Security Control. One of the following [DoD, Component, Enclave]",' + + '"naJustification": "Provide justification for Security Controls deemed Not Applicable to the system",' + + '"slcmCriticality": "Criticality of Security Control regarding system-level continuous monitoring (SLCM) ",' + + '"slcmFrequency": "One of the following [Constantly,Daily,Weekly,Monthly,Quarterly,Semi-Annually,Annually,Every Two Years,Every Three Years,Undetermined]",' + + '"slcmMethod": "One of the following [Automated, Semi-Automated, Manual, Undetermined]",' + + '"slcmReporting": "Organization/Office represented",' + + '"slcmTracking": "The System-Level Continuous Monitoring tracking",' + + '"slcmComments":" Additional comments for Security Control regarding SLCM"' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'controls-optional') { + const data = '{ ' + + '"implementationStatus": "One of the following [Planned,Implemented,Inherited,Not Applicable,Manually Inherited]",' + + '"severity": "One of the following [Very Low, Low, Moderate, High, Very High]",' + + '"vulnerabilitySummary": "Include vulnerability summary",' + + '"recommendations": "The include recommendations",' + + '"relevanceOfThreat": "One of the following [Very Low, Low, Moderate, High, Very High]",' + + '"likelihood": "One of the following [Very Low, Low, Moderate, High, Very High]",' + + '"impact": "One of the following [Very Low, Low, Moderate, High, Very High]",' + + '"impactDescription": "Include description of Security Controls impact",' + + '"residualRiskLevel": "One of the following [Very Low, Low, Moderate, High, Very High]",' + + '"testMethod": "One of the following [Test, Interview, Examine, Test,Interview, Test,Examine, Interview,Examine, Test,Interview,Examine]",' + + '"mitigations": "One of the following [Very Low, Low, Moderate, High, Very High]",' + + '"applicationLayer": "If the Financial Management (Navy) overlay is applied to the system, this field can be populated (Navy only)",' + + '"databaseLayer": "If the Financial Management (Navy) overlay is applied to the system, this field can be populated (Navy only)",' + + '"operatingSystemLayer": "If the Financial Management (Navy) overlay is applied to the system, this field can be populated (Navy only)"' + + '}' + return JSON.parse(data) + } + if (endpoint === 'poams-post-required') { const data = '{ ' + '"status": "One of the following: [Ongoing, Risk Accepted, Completed, Not Applicable]",' + '"vulnerabilityDescription": "POA&M vulnerability description",' + - '"sourceIdentVuln": "Source that identifies the vulnerability",' + + '"sourceIdentifyingVulnerability": "Source that identifies the vulnerability",' + '"pocOrganization": "Organization/Office represented",' + - '"mitigation": "Include mitigation explanation",' + '"resources": "List of resources used"' + '}' return JSON.parse(data) @@ -978,14 +1423,14 @@ export function getJsonExamples(endpoint?: string): string[] { '"milestones": [{' + '"description": "The milestone description",' + '"scheduledCompletionDate": "Milestone scheduled completion date (Unix format)"}],' + - '"pocFirstName": "The system acronym(s) e.g AC-1, AC-2",' + - '"pocLastName": "The system CCIS string numerical value",' + - '"pocEmail": "Security Checks that are associated with the POA&M",' + - '"pocPhoneNumber": "One of the following [I, II, III]",' + - '"severity": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"scheduledCompletionDate": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"completionDate": "Description of Security Control impact",' + - '"comments": "Description of the security control impact"' + + '"pocFirstName": "First name of POC (only if Last Name, Email, or Phone Number have data)",' + + '"pocLastName": "Last name of POC (only if First Name, Email, or Phone Number have data)",' + + '"pocEmail": "Email address of POC (only if First Name, Last Name, or Phone Number have data)",' + + '"pocPhoneNumber": "Phone number of POC (only if First Name, Last Name, or Email have data)",' + + '"severity": "Risk Analysis field, maybe required by certain eMASS instances. Required for approved items",' + + '"scheduledCompletionDate": "Required for ongoing and completed POA&M items",' + + '"completionDate": "Field is required for completed POA&M items",' + + '"comments": "Field is required for completed and risk accepted POA&M items"' + '}' return JSON.parse(data) } @@ -996,7 +1441,7 @@ export function getJsonExamples(endpoint?: string): string[] { '"displayPoamId": "Globally unique identifier for individual POA&M Items, seen on the front-end as ID",' + '"status": "One of the following: [Ongoing, Risk Accepted, Completed, Not Applicable]",' + '"vulnerabilityDescription": "POA&M vulnerability description",' + - '"sourceIdentVuln": "Source that identifies the vulnerability",' + + '"sourceIdentifyingVulnerability": "Source that identifies the vulnerability",' + '"pocOrganization": "Organization/Office represented",' + '"resources": "List of resources used"' + '}' @@ -1009,75 +1454,186 @@ export function getJsonExamples(endpoint?: string): string[] { '"milestoneId": "Unique milestone identifier",' + '"description": "The milestone description",' + '"scheduledCompletionDate": "Milestone scheduled completion date (Unix format)",' + - '"isActive": "To prevent uploading duplicate/undesired milestones through the POA&M PUT you must include an isActive field for the milestone and set it to equal to false"}],' + - '"pocFirstName": "The system acronym(s) e.g AC-1, AC-2",' + - '"pocLastName": "The system CCIS string numerical value",' + - '"pocEmail": "Security Checks that are associated with the POA&M",' + - '"pocPhoneNumber": "One of the following [I, II, III]",' + - '"severity": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"scheduledCompletionDate": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"completionDate": "Description of Security Control impact",' + - '"comments": "Description of the security control impact"' + + '"isActive": "To prevent uploading duplicate/undesired milestones through the POA&M PUT include the isActive=false. If absent or set to true a new Milestone is created"}],' + + '"pocFirstName": "First name of POC (only if Last Name, Email, or Phone Number have data)",' + + '"pocLastName": "Last name of POC (only if First Name, Email, or Phone Number have data)",' + + '"pocEmail": "Email address of POC (only if First Name, Last Name, or Phone Number have data)",' + + '"pocPhoneNumber": "Phone number of POC (only if First Name, Last Name, or Email have data)",' + + '"severity": "Risk Analysis field, maybe required by certain eMASS instances. Required for approved items",' + + '"scheduledCompletionDate": "POA&M Items with a review status of “Approved” and a status of “Completed” or “Ongoing” cannot update Scheduled Completion Date.",' + + '"completionDate": "Field is required for completed POA&M items",' + + '"comments": "Field is required for completed and risk accepted POA&M items"' + '}' return JSON.parse(data) } + if (endpoint === 'poams-post-put-required-va') { + const data = '{ ' + + '"identifiedInCFOAuditOrOtherReview": "If not specified, this field will be set to false because it does not accept a null value (Required for VA. Optional for Army and USCG)",' + + '"personnelResourcesFundedBaseHours": "Hours for personnel resources that are founded (Required for VA. Optional for Army and USCG)",' + + '"personnelResourcesCostCode": "Values are specific per eMASS instance (Required for VA. Optional for Army and USCG)",' + + '"personnelResourcesUnfundedBaseHours": "Funded based hours (100.00) (Required for VA. Optional for Army and USCG)",' + + '"personnelResourcesNonfundingObstacle": "Values are specific per eMASS instance (Required for VA. Optional for Army and USCG)",' + + '"personnelResourcesNonfundingObstacleOtherReason": "Reason (text 2,000 char) (Required for VA. Optional for Army and USCG)",' + + '"nonPersonnelResourcesFundedAmount": "Funded based hours (100.00) (Required for VA. Optional for Army and USCG)",' + + '"nonPersonnelResourcesCostCode": "Values are specific per eMASS instance (Required for VA. Optional for Army and USCG)",' + + '"nonPersonnelResourcesUnfundedAmount": "Funded based hours (100.00) (Required for VA. Optional for Army and USCG)",' + + '"nonPersonnelResourcesNonfundingObstacle": "Values are specific per eMASS instance (Required for VA. Optional for Army and USCG)",' + + '"nonPersonnelResourcesNonfundingObstacleOtherReason": "Reason (text 2,000 char) (Required for VA. Optional for Army and USCG)"' + + '}' + return JSON.parse(data) + } + if (endpoint === 'poams-post-put-optional') { const data = '{ ' + '"externalUid": "External ID associated with the POA&M",' + '"controlAcronym": "The system acronym(s) e.g AC-1, AC-2",' + - '"cci": "The system CCIS string numerical value",' + + '"assessmentProcedure": "The Security Control Assessment Procedures being associated with the POA&M Item",' + '"securityChecks": "Security Checks that are associated with the POA&M",' + - '"rawSeverity": "One of the following [I, II, III]",' + - '"relevanceOfThreat": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"likelihood": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"impact": "Description of Security Control impact",' + + '"rawSeverity": "One of the following [Very Low, Low, Moderate, High, Very High]",' + + '"relevanceOfThreat": "Risk Analysis field, maybe required by certain eMASS instances. One of the following [Very Low, Low, Moderate, High, Very High]",' + + '"likelihood": "Risk Analysis field, maybe required by certain eMASS instances. One of the following [Very Low, Low, Moderate, High, Very High]",' + + '"impact": "Risk Analysis field, maybe required by certain eMASS instances. Description of Security Control impact",' + + '"residualRiskLevel": "Risk Analysis field, maybe required by certain eMASS instances. One of the following [Very Low, Low, Moderate, High, Very High]",' + + '"mitigations": "Risk Analysis field, maybe required by certain eMASS instances. Mitigation explanation",' + '"impactDescription": "Description of the security control impact",' + - '"residualRiskLevel": "One of the following [Very Low, Low, Moderate, High, Very High]",' + '"recommendations": "Any recommendations content",' + - '"mitigation": "Mitigation explanation"' + + '"resultingResidualRiskLevelAfterProposedMitigations": "One of the following [Very Low, Low, Moderate, High, Very High] (Navy only)",' + + '"predisposingConditions": "Conditions (Navy only)",' + + '"threatDescription": "Threat description (Navy only)",' + + '"devicesAffected": "List of affected devices by hostname. If all devices are affected, use `system` or `all` (Navy only)"' + '}' return JSON.parse(data) } - if (endpoint === 'controls-required') { + if (endpoint === 'artifacts-put-required') { const data = '{ ' + - '"acronym": "System acronym, required to match the NIST SP 800-53 Revision 4.",' + - '"responsibleEntities": "Include written description of Responsible Entities that are responsible for the Security Control.",' + - '"controlDesignation": "One of the following: [Common, System-Specific, Hybrid]",' + - '"estimatedCompletionDate": "Field is required for Implementation Plan",' + - '"implementationNarrative": "Includes Security Control comments"' + + '"filename": "Artifact file name to update for the given system",' + + '"isTemplate": "Indicates whether an artifact is a template",' + + '"type": "The type of artifact. Possible values are: Procedure, Diagram, Policy, Labor, Document, Image, Other, Scan Result, Auditor Report. May accept other values set by system administrators",' + + '"category": "Artifact category. Possible values are: Implementation Guidance or Evidence. May accept other values set by system administrators"' + '}' return JSON.parse(data) } - if (endpoint === 'controls-conditional') { + if (endpoint === 'artifacts-put-optional') { const data = '{ ' + - '"commonControlProvider": "One of the following [DoD, Component, Enclave]",' + - '"naJustification": "Provide justification for Security Controls deemed Not Applicable to the system",' + - '"slcmCriticality": "Criticality of Security Control regarding SLCM",' + - '"slcmFrequency": "One of the following [Constantly,Daily,Weekly,Monthly,Quarterly,Semi-Annually,Annually,Every Two Years,Every Three Years,Undetermined]",' + - '"slcmMethod": "One of the following [Automated, Semi-Automated, Manual, Undetermined]",' + - '"slcmReporting": "Organization/Office represented",' + - '"slcmTracking": "The System-Level Continuous Monitoring tracking",' + - '"slcmComments":" Additional comments for Security Control regarding SLCM"' + + '"name": "The artifact name",' + + '"artifactDescription": "The artifact(s) description",' + + '"refPageNumber": "Artifact reference page number",' + + '"controls": "Control acronym associated with the artifact. NIST SP 800-53 Revision 4 defined",' + + '"assessmentProcedures": "The Security Control Assessment Procedure being associated with the artifact",' + + '"expirationDate": "Date artifact expires and requires review",' + + '"lastReviewDate": "Date artifact was last reviewed",' + + '"signedDate": "Date artifact was signed"' + '}' return JSON.parse(data) } - if (endpoint === 'controls-optional') { + if (endpoint === 'hardware-post-required') { const data = '{ ' + - '"implementationStatus": "One of the following [Planned,Implemented,Inherited,Not Applicable,Manually Inherited]",' + - '"severity": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"vulnerabilitySummary": "Include vulnerability summary",' + - '"recommendations": "The include recommendations",' + - '"relevanceOfThreat": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"likelihood": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"impact": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"impactDescription": "Include description of Security Controls impact",' + - '"residualRiskLevel": "One of the following [Very Low, Low, Moderate, High, Very High]",' + - '"testMethod": "One of the following [Test, Interview, Examine, Test,Interview, Test,Examine, Interview,Examine, Test,Interview,Examine]"' + - '}' + '"assetName": "Name of the hardware asset"' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'hardware-put-required') { + const data = '{ ' + + '"hardwareId": "GUID identifying the specific hardware asset",' + + '"assetName": "Name of the hardware asset"' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'hardware-post-put-conditional') { + const data = '{ ' + + '"publicFacingFqdn": "Public facing FQDN. Only applicable if Public Facing is set to true",' + + '"publicFacingIpAddress": "Public facing IP address. Only applicable if Public Facing is set to true",' + + '"publicFacingUrls": "Public facing URL(s). Only applicable if Public Facing is set to true"' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'hardware-post-put-optional') { + const data = '{ ' + + '"componentType": "Public facing FQDN. Only applicable if Public Facing is set to true",' + + '"nickname": "Public facing IP address. Only applicable if Public Facing is set to true",' + + '"assetIpAddress": "IP address of the hardware asset",' + + '"publicFacing": "Public facing is defined as any asset that is accessible from a commercial connection",' + + '"virtualAsset": "Determine if this is a virtual hardware asset",' + + '"manufacturer": "Manufacturer of the hardware asset. Populated with “Virtual” by default if Virtual Asset is true",' + + '"modelNumber": "Model number of the hardware asset. Populated with “Virtual” by default if Virtual Asset is true",' + + '"serialNumber": "Serial number of the hardware asset. Populated with “Virtual” by default if Virtual Asset is true",' + + '"osIosFwVersion": "OS/iOS/FW version of the hardware asset",' + + '"memorySizeType": "Memory size / type of the hardware asset",' + + '"location": "Location of the hardware asset",' + + '"approvalStatus": "Approval status of the hardware asset",' + + '"criticalAsset": "Indicates whether the asset is a critical information system asset"' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'software-post-required') { + const data = '{ ' + + '"softwareVendor": "Vendor of the software asset",' + + '"softwareName": "Name of the software asset",' + + '"version": "Version of the software asset"' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'software-put-required') { + const data = '{ ' + + '"softwareId": "GUID identifying the specific software asset",' + + '"softwareVendor": "Vendor of the software asset",' + + '"softwareName": "Name of the software asset",' + + '"version": "Version of the software asset"' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'software-post-put-conditional') { + const data = '{ ' + + '"approvalDate": "Approval date of the software asset. If Approval Status is set to “Unapproved” or “In Progress”, Approval Date will be set to null"' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'software-post-put-optional') { + const data = '{ ' + + '"softwareType": "Type of the software asset",' + + '"parentSystem": "Parent system of the software asset",' + + '"subsystem": "Subsystem of the software asset",' + + '"network": "Network of the software asset",' + + '"hostingEnvironment": "Hosting environment of the software asset",' + + '"softwareDependencies": "Dependencies for the software asset",' + + '"cryptographicHash": "Cryptographic hash for the software asset",' + + '"inServiceData": "Date the sotware asset was added to the network",' + + '"itBudgetUii": "IT budget UII for the software asset",' + + '"fiscalYear": "Fiscal year (FY) for the software asset",' + + '"popEndDate": "Period of performance (POP) end date for the software asset",' + + '"licenseOrContract": "License or contract for the software asset",' + + '"licenseTerm": "License term for the software asset",' + + '"costPerLicense": "Cost per license for the software asset",' + + '"totalLicenses": "Number of total licenses for the software asset",' + + '"totalLicenseCost": "Total cost of the licenses for the software asset",' + + '"licensesUsed": "Number of licenses used for the software asset",' + + '"licensePoc": "Point of contact (POC) for the software asset",' + + '"licenseRenewalDate": "License renewal date for the software asset",' + + '"licenseExpirationDate": "License expiration date for the software asset",' + + '"approvalStatus": "Approval status of the software asset",' + + '"releaseDate": "Release date of the software asset",' + + '"maintenanceDate": "Maintenance date of the software asset",' + + '"retirementDate": "Retirement date of the software asset",' + + '"endOfLifeSupportDate": "End of life/support date of the software asset",' + + '"extendedEndOfLifeSupportDate": "Extended End of Life/Support Date cannot occur prior to the End of Life/Support Date",' + + '"criticalAsset": "Indicates whether the asset is a critical information system asset",' + + '"location": "Location of the software asset",' + + '"purpose": "Purpose of the software asset",' + + '"unsupportedOperatingSystem": "Unsupported operating system (VA only)",' + + '"unapprovedSoftwareFromTrm": "Unapproved software from TRM (VA only)",' + + '"approvedWaiver": "Approved waiver (VA only)"' + + '}' return JSON.parse(data) } @@ -1115,36 +1671,6 @@ export function getJsonExamples(endpoint?: string): string[] { return JSON.parse(data) } - if (endpoint === 'scan_findings-application') { - const data = '{ ' + - '"application": {' + - '"applicationName": "Name of the software application that was assessed",' + - '"version": "The version of the application"}' + - '}' - return JSON.parse(data) - } - - if (endpoint === 'scan_findings-applicationFindings') { - const data = '{ ' + - '"applicationFindings": [{' + - '"codeCheckName": "Name of the software vulnerability or weakness",' + - '"scanDate": "The scan date, Unix date format",' + - '"resourceName": "Friendly name of Cloud resource",' + - '"cweId": "The Common Weakness Enumerator (CWE) identifier",' + - '"count": "Number of instances observed for a specified finding",' + - '"rawSeverity": "OPTIONAL - One of the following [Low, Medium, Moderate, High, Critical]"}]' + - '}' - return JSON.parse(data) - } - - if (endpoint === 'scan_findings-clearFindings') { - const data = '{ ' + - '"applicationFindings": [{' + - '"clearFindings": "To clear an application\'s findings, use only the field clearFindings and set it to true."}]' + - '}' - return JSON.parse(data) - } - if (endpoint === 'container_scans-required') { const data = '{ ' + '"containerId": "Unique identifier of the container",' + @@ -1170,6 +1696,8 @@ export function getJsonExamples(endpoint?: string): string[] { '"test": "Informational tags associated to results for other metadata" },' + '"benchmarks": [{' + '"isBaseline": "True/false flag for providing results as baseline. If true, all existing compliance results for the provided benchmark within the container will be replaced by results in the current call", ' + + '"verion": "The benchmark version", ' + + '"release": "The benchmark release", ' + '"results": [{' + '"message": "Comments for the result"}]' + '}]' + @@ -1177,5 +1705,80 @@ export function getJsonExamples(endpoint?: string): string[] { return JSON.parse(data) } + if (endpoint === 'scan_findings-application') { + const data = '{ ' + + '"application": {' + + '"applicationName": "Name of the software application that was assessed",' + + '"version": "The version of the application"}' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'scan_findings-applicationFindings') { + const data = '{ ' + + '"applicationFindings": [{' + + '"codeCheckName": "Name of the software vulnerability or weakness",' + + '"scanDate": "The scan date, Unix date format",' + + '"cweId": "The Common Weakness Enumerator (CWE) identifier",' + + '"count": "Number of instances observed for a specified finding",' + + '"rawSeverity": "OPTIONAL - One of the following [Low, Medium, Moderate, High, Critical]"}]' + + '}' + return JSON.parse(data) + } + + if (endpoint === 'scan_findings-clearFindings') { + const data = '{ ' + + '"applicationFindings": [{' + + '"clearFindings": "To clear an application\'s findings, use only the field clearFindings and set it to true."}]' + + '}' + return JSON.parse(data) + } + return [] } + +/** + * Saves data to a specified file within a given directory. If the directory does not exist, it will be created. + * + * @param dir - The directory where the file will be saved. + * @param filename - The name of the file to save the data to. + * @param data - The data to be saved in the file. + * + * @remarks + * This function checks if the specified directory exists, and if not, it creates the directory. + * It then writes the provided data to a file within that directory. If an error occurs during + * the file writing process, an error message is logged to the console. + */ +export function saveFile(dir: string, filename: string, data: any): void { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir) + } + + // Save to file + const outDir = path.join(dir, filename) + fs.writeFile(outDir, data, err => { + if (err) { + console.error(`Error saving file to: ${outDir}. Cause: ${err}`) + } + }) +} + +/** + * Prints a help message to the console with a specific format. + * + * The message is prefixed with an arrow (→) and displayed in yellow color. + * + * @param msg - The help message to be printed. + */ +export function printHelpMsg(msg: string) { + console.log('\x1B[93m\n→', msg, '\x1B[0m') +} + +/** + * Prints a message to the console in red color. + * + * @param msg - The message to be printed. + */ +export function printRedMsg(msg: string) { + console.log('\x1B[91m»', msg, '\x1B[0m') +} diff --git a/src/utils/oclif/hooks/command_not_found.ts b/src/utils/oclif/hooks/command_not_found.ts new file mode 100644 index 000000000..1442f52ae --- /dev/null +++ b/src/utils/oclif/hooks/command_not_found.ts @@ -0,0 +1,28 @@ +import {Hook} from '@oclif/core' + +/** + * Hook that is triggered when an "oclif" command is not found. + * + * @param opts - The options object containing the command ID. + * @param opts.id - The ID of the command that was not found. + * + * @returns A promise that logs (outputs) an error message and exits the + * process with a status code of 1. + * + * @example + * Invoking a non existing command like: + * $ saf emasser puts + * It will result in oclif calling the command_not_found hook (this hook) + * because there isn't a puts command, the command is put. + * + * However, it the command is: + * $ saf emasser puts -h + * This hook is not called + */ +const commandNotFound: Hook<'command_not_found'> = async (opts: { id: string }) => { // skipcq: JS-0116 + console.error(`\x1B[91m» Command "${opts.id}" not found.\x1B[0m`) + console.error(`\x1B[93m→ Try this 👉 \x1B[92m"${opts.id.split(':')[0]} [-h or --help]" \x1B[93mfor additional help with the command.\x1B[0m`) + process.exit(1) +} + +export default commandNotFound diff --git a/src/utils/threshold.ts b/src/utils/threshold.ts index c2f8d77d4..69753f074 100644 --- a/src/utils/threshold.ts +++ b/src/utils/threshold.ts @@ -104,8 +104,21 @@ export function calculateCompliance(statusHash: StatusHash): number { return Math.round((100 * statusHash.Passed) / total) } +/** + * This function does not exit the process, it rather evaluates if an error occurred + * logs an error message to the console and throws an error with the provided reason. + * + * It is the responsibility of the caller to catch the error and exit accordantly. + * + * @param condition - The condition to evaluate. If true, the process will exit with an error. + * @param reason - Optional. The reason for the error. If not provided, a default message will be used. + * @throws Will throw an error with the provided reason or a default message if the condition is true. + * + * @returns - does not return, it simply trows an error if condition is satisfied (true) + */ export function exitNonZeroIfTrue(condition: boolean, reason?: string) { if (condition) { + console.error(`Error: ${reason}` || 'Error: Compliance levels were not met') // skipcq: JS-W1043 throw new Error(reason || 'Compliance levels were not met') } } diff --git a/test/commands/attest/apply.test.ts b/test/commands/attest/apply.test.ts index badecc546..341b9a9c6 100644 --- a/test/commands/attest/apply.test.ts +++ b/test/commands/attest/apply.test.ts @@ -1,41 +1,103 @@ /* eslint-disable array-bracket-newline */ /* eslint-disable array-element-newline */ -import {expect} from 'chai' -import {runCommand} from '@oclif/test' -import tmp from 'tmp' -import path from 'path' -import fs from 'fs' -import {omitHDFChangingFields} from '../utils' +import { expect } from 'chai'; +import { runCommand } from '@oclif/test'; +import tmp from 'tmp'; +import path from 'path'; +import fs from 'fs'; +import { omitHDFChangingFields } from '../utils'; describe('Test attest apply', () => { - const tmpobj = tmp.dirSync({unsafeCleanup: true}) + let tmpobj; - // NOTE: replacing all CR from the files being generated to ensure proper comparison. - it('Successfully applies a JSON attestations file', async () => { - await runCommand<{name: string}>(['attest apply', - '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), - path.resolve('./test/sample_data/attestations/attestations_jsonFormat.json'), - '-o', `${tmpobj.name}/rhel8_attestations_jsonOutput.json`, - ]) - const output = JSON.parse(fs.readFileSync(`${tmpobj.name}/rhel8_attestations_jsonOutput.json`, 'utf8').replaceAll(/\r/gi, '')) - const expected = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), 'utf8').replaceAll(/\r/gi, '')) + before(() => { + tmpobj = tmp.dirSync({ unsafeCleanup: true }); + }); - expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected)) - }) + after(() => { + tmpobj.removeCallback(); + }); - it('Successfully applies an XLSX attestations file', async () => { - await runCommand<{name: string}>(['attest apply', '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), path.resolve('./test/sample_data/attestations/attestations_xlsxFormat.xlsx'), '-o', `${tmpobj.name}/rhel8_attestations_xlsxOutput.json`]) - const output = JSON.parse(fs.readFileSync(`${tmpobj.name}/rhel8_attestations_xlsxOutput.json`, 'utf8').replaceAll(/\r/gi, '')) - const expected = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), 'utf8').replaceAll(/\r/gi, '')) + const captureOpts = { + print: true, + stripAnsi: false, + }; + + const readAndParseJSON = (filePath) => { + return JSON.parse(fs.readFileSync(filePath, 'utf8').replaceAll(/\r/gi, '')); + }; - expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected)) - }) + const runAndValidate = async (commandArgs, outputFilePath, expectedFilePath) => { + const { stderr } = await runCommand(commandArgs, undefined, captureOpts); + const output = readAndParseJSON(outputFilePath); + const expected = readAndParseJSON(expectedFilePath); + + expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected)); + expect(stderr).to.be.empty; + }; + + it('Successfully applies a JSON attestations file', async () => { + await runAndValidate( + [ + 'attest apply', + '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), + path.resolve('./test/sample_data/attestations/attestations_jsonFormat.json'), + '-o', `${tmpobj.name}/rhel8_attestations_jsonOutput.json`, + ], + `${tmpobj.name}/rhel8_attestations_jsonOutput.json`, + path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json') + ); + }); + + it('Successfully applies an XLSX attestations file', async () => { + await runAndValidate( + [ + 'attest apply', + '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), + path.resolve('./test/sample_data/attestations/attestations_xlsxFormat.xlsx'), + '-o', `${tmpobj.name}/rhel8_attestations_xlsxOutput.json`, + ], + `${tmpobj.name}/rhel8_attestations_xlsxOutput.json`, + path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json') + ); + }); it('Successfully applies a YAML attestations file', async () => { - await runCommand<{name: string}>(['attest apply', '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), path.resolve('./test/sample_data/attestations/attestations_yamlFormat.yaml'), '-o', `${tmpobj.name}/rhel8_attestations_yamlOutput.json`]) - const output = JSON.parse(fs.readFileSync(`${tmpobj.name}/rhel8_attestations_yamlOutput.json`, 'utf8').replaceAll(/\r/gi, '')) - const expected = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), 'utf8').replaceAll(/\r/gi, '')) + await runAndValidate( + [ + 'attest apply', + '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), + path.resolve('./test/sample_data/attestations/attestations_yamlFormat.yaml'), + '-o', `${tmpobj.name}/rhel8_attestations_yamlOutput.json`, + ], + `${tmpobj.name}/rhel8_attestations_yamlOutput.json`, + path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json') + ); + }); + + it('Successfully applies JSON attestations file to an overlay profile', async () => { + await runAndValidate( + [ + 'attest apply', + '-i', path.resolve('./test/sample_data/HDF/input/triple_overlay_profile_sample.json'), + path.resolve('./test/sample_data/attestations/triple_overlay_example-attestations.json'), + '-o', `${tmpobj.name}/triple_overlay_attested.json`, + ], + `${tmpobj.name}/triple_overlay_attested.json`, + path.resolve('./test/sample_data/attestations/triple_overlay_attested.json') + ); + }); - expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected)) - }) -}) + it('Successfully applies a YAML attestations file to an overlay profile', async () => { + await runAndValidate( + [ + 'attest apply', + '-i', path.resolve('./test/sample_data/HDF/input/triple_overlay_profile_sample.json'), + path.resolve('./test/sample_data/attestations/triple_overlay_example-attestations.yml'), + '-o', `${tmpobj.name}/triple_overlay_attested_with_yml.json`, + ], + `${tmpobj.name}/triple_overlay_attested_with_yml.json`, + path.resolve('./test/sample_data/attestations/triple_overlay_attested.json') + ); + }); +}); diff --git a/test/commands/convert/ckl2hdf.test.ts b/test/commands/convert/ckl2hdf.test.ts index 7f0c4930b..e559ab458 100644 --- a/test/commands/convert/ckl2hdf.test.ts +++ b/test/commands/convert/ckl2hdf.test.ts @@ -72,6 +72,8 @@ describe('Test invalid checklist metadata example', () => { '-i', path.resolve('./test/sample_data/checklist/sample_input_report/ckl_with_invalid_metadata.ckl'), '-o', `${tmpobj.name}/invalid_output.json`, ]) - expect(stderr).to.equal('Error converting to hdf:\nError: Invalid checklist metadata fields:\n\tHost FQDN (invalid)\n\tHost IP (invalid)\n\tHost MAC (invalid)\n') + const expected_error_message = 'Error converting to hdf:\nError: Invalid checklist metadata fields:\n\tHost FQDN (invalid)\n\tHost IP (invalid)\n\tHost MAC addresses must be valid and separated by newline, space, or comma. (invalid)\n' + // const expected_error_message2 = 'Error converting to hdf:\nError: Invalid checklist metadata fields:\n\tHost FQDN (invalid)\n\tHost IP (invalid)\n\tHost MAC (invalid)\n' + expect(stderr).to.equal(expected_error_message) }) }) diff --git a/test/commands/convert/trufflehog2hdf.test.ts b/test/commands/convert/trufflehog2hdf.test.ts index 82c43f55c..dfde9d86e 100644 --- a/test/commands/convert/trufflehog2hdf.test.ts +++ b/test/commands/convert/trufflehog2hdf.test.ts @@ -49,6 +49,16 @@ describe('Test Trufflehog', () => { const sample = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/trufflehog/trufflehog-saf-hdf.json'), 'utf8')) expect(omitHDFChangingFields(converted)).to.eql(omitHDFChangingFields(sample)) }) + + it('hdf-converter output test - ndjson and duplicate finding', async () => { + await runCommand<{name: string}>(['convert trufflehog2hdf', + '-i', path.resolve('./test/sample_data/trufflehog/sample_input_report/trufflehog_dup.ndjson'), + '-o', `${tmpobj.name}/trufflehog.json`, + ]) + const converted = JSON.parse(fs.readFileSync(`${tmpobj.name}/trufflehog.json`, 'utf8')) + const sample = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/trufflehog/trufflehog-ndjson-dup-hdf.json'), 'utf8')) + expect(omitHDFChangingFields(converted)).to.eql(omitHDFChangingFields(sample)) + }) }) describe('Test Trufflehog using withraw flag', () => { diff --git a/test/commands/emasser/delete.test.ts b/test/commands/emasser/delete.test.ts index 34c6d5a06..fadab1108 100644 --- a/test/commands/emasser/delete.test.ts +++ b/test/commands/emasser/delete.test.ts @@ -1,19 +1,25 @@ import {expect} from 'chai' import {InitMockServer} from './mock.server' -import {ArtifactsApi, POAMApi, MilestonesApi} from '@mitre/emass_client' -import {ArtifactsResponseDel, PoamResponseDelete, - MilestonesPutPostDelete} from '@mitre/emass_client/dist/api' +import { + ArtifactsApi, POAMApi, + MilestonesApi, CloudResourceResultsApi, + ContainerScanResultsApi, HardwareBaselineApi, + SoftwareBaselineApi, +} from '@mitre/emass_client' +import { + ArtifactsResponseDel, PoamResponsePostPutDelete, + MilestonesPutPostDelete, ContainersResourcesPostDelete, + CloudResourcesPostDelete, HwBaselineResponseDelete, + SwBaselineResponseDelete, +} from '@mitre/emass_client/dist/api' -describe('Test eMASS API CLI (delete) commands', () => { - const mocSer = new InitMockServer() +describe('Test eMASSer API CLI (DELETE) commands', () => { + const mocServer = new InitMockServer() let responseDataObj: Map - const testOk = { - status: 200, - statusText: 'OK', - } + const testOk = {status: 200, statusText: 'OK'} - it('Successfully tested endpoint - artifacts', async () => { - const delArtifact = new ArtifactsApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Artifacts', async () => { + const delArtifact = new ArtifactsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await delArtifact.deleteArtifact(123, []).then((response: ArtifactsResponseDel) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -27,9 +33,9 @@ describe('Test eMASS API CLI (delete) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - poams', async () => { - const delPoam = new POAMApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await delPoam.deletePoam(34, []).then((response: PoamResponseDelete) => { + it('Successfully tested endpoint - Cloud Resources', async () => { + const delCloudResources = new CloudResourceResultsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await delCloudResources.deleteCloudResources(123, []).then((response: CloudResourcesPostDelete) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -42,8 +48,53 @@ describe('Test eMASS API CLI (delete) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - milestones', async () => { - const delMilestones = new MilestonesApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Container Scans', async () => { + const delContainerScans = new ContainerScanResultsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await delContainerScans.deleteContainerSans(123, []).then((response: ContainersResourcesPostDelete) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') + }) + + it('Successfully tested endpoint - Hardware Baseline', async () => { + const delHwBaseline = new HardwareBaselineApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await delHwBaseline.deleteHwBaselineAssets(123, []).then((response: HwBaselineResponseDelete) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') + }) + + it('Successfully tested endpoint - Software Baseline', async () => { + const delSwBaseline = new SoftwareBaselineApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await delSwBaseline.deleteSwBaselineAssets(123, []).then((response: SwBaselineResponseDelete) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') + }) + + it('Successfully tested endpoint - Milestones', async () => { + const delMilestones = new MilestonesApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await delMilestones.deleteMilestone(36, 76, []).then((response: MilestonesPutPostDelete) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -56,4 +107,19 @@ describe('Test eMASS API CLI (delete) commands', () => { expect(responseDataObj.get('status')).to.equal(200) expect(responseDataObj.get('statusText')).to.equal('OK') }) + + it('Successfully tested endpoint - POA&Ms', async () => { + const delPoam = new POAMApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await delPoam.deletePoam(34, []).then((response: PoamResponsePostPutDelete) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') + }) }) diff --git a/test/commands/emasser/get.test.ts b/test/commands/emasser/get.test.ts index 5b32134f5..8141a1030 100644 --- a/test/commands/emasser/get.test.ts +++ b/test/commands/emasser/get.test.ts @@ -1,22 +1,49 @@ import {expect} from 'chai' import {InitMockServer} from './mock.server' -import {SystemsApi, SystemRolesApi, TestApi, - POAMApi, CACApi, PACApi, MilestonesApi, ControlsApi, - DashboardsApi} from '@mitre/emass_client' -import {PoamResponseGetPoams, SystemResponse, - SystemRolesResponse, SystemsResponse, CacResponseGet, - PacResponseGet, MilestoneResponseGetMilestone} from '@mitre/emass_client/dist/api' - -describe('Test eMASS API CLI (get) commands', () => { - const mocSer = new InitMockServer() +import { + SystemsApi, SystemRolesApi, TestApi, + POAMApi, CACApi, PACApi, MilestonesApi, + ControlsApi, SystemStatusDashboardApi, + TestResultsApi, ArtifactsApi, + ArtifactsExportApi, HardwareBaselineApi, + WorkflowInstancesApi, CMMCAssessmentsApi, + SoftwareBaselineApi, WorkflowDefinitionsApi, + SystemSecurityControlsDashboardsApi, + SystemTermsConditionsDashboardsApi, + SystemConnectivityCCSDDashboardsApi, + SystemATCIATCDashboardApi, SystemQuestionnaireDashboardsApi, + SystemWorkflowsDashboardsApi, SystemPOAMDashboardsApi, + SystemArtifactsDashboardsApi, SystemHardwareDashboardsApi, + SystemSensorHardwareDashboardsApi, SystemSoftwareDashboardsApi, + SystemSensorSoftwareDashboardsApi, SystemCriticalAssetsDashboardApi, + SystemVulnerabilityDashboardApi, SystemDeviceFindingsDashboardsApi, + SystemApplicationFindingsDashboardsApi, SystemPortsProtocolsDashboardsApi, + SystemCONMONIntegrationStatusDashboardApi, + SystemAssociationsDashboardApi, UserSystemAssignmentsDashboardApi, + OrganizationMigrationStatusDashboardApi, SystemMigrationStatusDashboardApi, + VAOMBFISMADashboardApi, SystemPrivacyDashboardApi, + CoastGuardSystemFISMAMetricsDashboardApi, SystemFISMAMetricsDashboardApi, + VASystemDashboardsApi, CMMCAssessmentDashboardsApi, +} from '@mitre/emass_client' + +import { + PoamResponseGetPoams, PoamResponseGetSystems, + SystemResponse, SystemRolesResponse, SystemsResponse, + CacResponseGet, PacResponseGet, MilestoneResponseGetMilestone, + MilestoneResponseGet, TestResultsResponseGet, + ArtifactsResponseGet, HwBaselineResponseGet, + SwBaselineResponseGet, WorkflowDefinitionResponseGet, + WorkflowInstancesResponseGet, WorkflowInstanceResponseGet, + CmmcResponseGet, +} from '@mitre/emass_client/dist/api' + +describe('Test eMASSer API CLI (GET) commands', () => { + const mocServer = new InitMockServer() let responseDataObj: Map - const testOk = { - status: 200, - statusText: 'OK', - } + const testOk = {status: 200, statusText: 'OK'} - it('Successfully tested endpoint - test connection', async () => { - const getTestApi = new TestApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Test Connection', async () => { + const getTestApi = new TestApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await getTestApi.testConnection().then((response: SystemResponse) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -30,9 +57,9 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - system', async () => { - const getSystems = new SystemsApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await getSystems.getSystem(123, true, 'rmf').then((response: SystemResponse) => { + it('Successfully tested endpoint - System', async () => { + const getSystems = new SystemsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getSystems.getSystem(123).then((response: SystemResponse) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -45,9 +72,9 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - systems', async () => { - const getSystems = new SystemsApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await getSystems.getSystems(false, 'regular', 'ditprid', 'coamsid', 'rmf', false, false, true).then((response: SystemsResponse) => { + it('Successfully tested endpoint - Systems', async () => { + const getSystems = new SystemsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getSystems.getSystems().then((response: SystemsResponse) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -60,8 +87,8 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - roles', async () => { - const getSystemRoles = new SystemRolesApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Roles', async () => { + const getSystemRoles = new SystemRolesApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await getSystemRoles.getSystemRoles().then((response: SystemRolesResponse) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -75,9 +102,9 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - poams', async () => { - const getPoams = new POAMApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await getPoams.getSystemPoamsByPoamId(34, 56).then((response: PoamResponseGetPoams) => { + it('Successfully tested endpoint - Controls', async () => { + const getControls = new ControlsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getControls.getSystemControls(34, 'acronym').then((response: CacResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -90,9 +117,9 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - cac', async () => { - const getCac = new CACApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await getCac.getSystemCac(34, 'acronym').then((response: CacResponseGet) => { + it('Successfully tested endpoint - Test Results', async () => { + const getControls = new TestResultsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getControls.getSystemTestResults(34, 'acronym').then((response: TestResultsResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -105,9 +132,9 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - pac', async () => { - const getPac = new PACApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await getPac.getSystemPac(34).then((response: PacResponseGet) => { + it('Successfully tested endpoint - POA&Ms (for system)', async () => { + const getPoams = new POAMApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getPoams.getSystemPoams(34, 56).then((response: PoamResponseGetSystems) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -120,9 +147,9 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - milestones', async () => { - const getMilestones = new MilestonesApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await getMilestones.getSystemMilestonesByPoamIdAndMilestoneId(36, 76, 89).then((response: MilestoneResponseGetMilestone) => { + it('Successfully tested endpoint - POA&Ms (for poam Id)', async () => { + const getPoams = new POAMApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getPoams.getSystemPoamsByPoamId(34, 56).then((response: PoamResponseGetPoams) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -135,9 +162,9 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - controls', async () => { - const getControls = new ControlsApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await getControls.getSystemControls(34, 'acronym').then((response: CacResponseGet) => { + it('Successfully tested endpoint - Milestones (for poam Id', async () => { + const getMilestones = new MilestonesApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getMilestones.getSystemMilestonesByPoamId(36, 76, 89).then((response: MilestoneResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -150,210 +177,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - const getDashboards = new DashboardsApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - - it('Successfully tested endpoint - dashboards (status_details)', async () => { - await getDashboards.getSystemStatusDetails(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (control_compliance_summary)', async () => { - await getDashboards.getSystemControlComplianceSummary(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (security_control_details)', async () => { - await getDashboards.getSystemSecurityControlDetails(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (assessment_procedures_details)', async () => { - await getDashboards.getSystemAssessmentProceduresDetails(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (poam_summary)', async () => { - await getDashboards.getSystemPoamSummary(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (poam_details)', async () => { - await getDashboards.getSystemPoamDetails(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (artifacts_summary)', async () => { - await getDashboards.getSystemArtifactsSummary(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (artifacts_details)', async () => { - await getDashboards.getSystemArtifactsDetails(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (hardware_summary)', async () => { - await getDashboards.getSystemHardwareSummary(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (hardware_details)', async () => { - await getDashboards.getSystemHardwareDetails(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (sensor_hardware_summary)', async () => { - await getDashboards.getSystemSensorHardwareSummary(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (sensor_hardware_details)', async () => { - await getDashboards.getSystemSensorHardwareDetails(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (ports_protocols_summary)', async () => { - await getDashboards.getSystemPortsProtocolsSummary(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (ports_protocols_details)', async () => { - await getDashboards.getSystemPortsProtocolsDetails(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) - }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) - - it('Successfully tested endpoint - dashboards (associations_details)', async () => { - await getDashboards.getSystemAssociationsDetails(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - Milestones (for poamId & milestone Id', async () => { + const getMilestones = new MilestonesApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getMilestones.getSystemMilestonesByPoamIdAndMilestoneId(36, 76, 89).then((response: MilestoneResponseGetMilestone) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -362,12 +192,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (assignments_details)', async () => { - await getDashboards.getUserSystemAssignmentsDetails(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - Artifacts', async () => { + const getArtifacs = new ArtifactsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getArtifacs.getSystemArtifacts(34, 56).then((response: ArtifactsResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -376,12 +207,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (privacy_summary)', async () => { - await getDashboards.getSystemPrivacySummary(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - Artifacts (export)', async () => { + const getArtifacs = new ArtifactsExportApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getArtifacs.getSystemArtifactsExport(34, 'thisfile', false).then((response: any) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -390,12 +222,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (fisma_saop_summary)', async () => { - await getDashboards.getVaOmbFsmaSaopSummary(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - CAC', async () => { + const getCac = new CACApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getCac.getSystemCac(34, 'acronym').then((response: CacResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -404,12 +237,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (va_aa_summary)', async () => { - await getDashboards.getVaSystemAaSummary(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - PAC', async () => { + const getPac = new PACApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getPac.getSystemPac(34).then((response: PacResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -418,12 +252,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (va_a2_summary)', async () => { - await getDashboards.getVaSystemA2Summary(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - Hardware Baseline', async () => { + const hardware = new HardwareBaselineApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await hardware.getSystemHwBaseline(1, 2, 3).then((response: HwBaselineResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -432,12 +267,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (va_pl_109_summary)', async () => { - await getDashboards.getVaSystemPl109ReportingSummary(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - Software Baseline', async () => { + const software = new SoftwareBaselineApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await software.getSystemSwBaseline(34).then((response: SwBaselineResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -446,12 +282,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (fisma_inventory_summary)', async () => { - await getDashboards.getVaSystemFismaInvetorySummary(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - Workflow Definitions', async () => { + const apiCon = new WorkflowDefinitionsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await apiCon.getWorkflowDefinitions(false, 'type').then((response: WorkflowDefinitionResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -460,12 +297,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (fisma_inventory_crypto_summary)', async () => { - await getDashboards.getVaSystemFismaInvetoryCryptoSummary(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - Workflow Instances (all)', async () => { + const apiCon = new WorkflowInstancesApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await apiCon.getSystemWorkflowInstances(false, false, 1, 2).then((response: WorkflowInstancesResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -474,12 +312,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (threat_risk_summary)', async () => { - await getDashboards.getVaSystemThreatRiskSummary(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - Workflow Instances (for workflow Id)', async () => { + const apiCon = new WorkflowInstancesApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await apiCon.getSystemWorkflowInstancesByWorkflowInstanceId(2).then((response: WorkflowInstanceResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -488,12 +327,13 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (threat_source_details)', async () => { - await getDashboards.getVaSystemThreatSourceDetails(45, false, 1, 2000).then((response: object) => { + it('Successfully tested endpoint - CMMC Assessment', async () => { + const apiCon = new CMMCAssessmentsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await apiCon.getCmmcAssessments(34).then((response: CmmcResponseGet) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) + console.error(error) } responseDataObj = new Map(Object.entries(testOk)) @@ -502,17 +342,82 @@ describe('Test eMASS API CLI (get) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - dashboards (threat_architecture_details)', async () => { - await getDashboards.getVaSystemThreatArchitectureDetails(45, false, 1, 2000).then((response: object) => { - responseDataObj = new Map(Object.entries(response)) - }).catch((error:any) => { - if (error.message.includes('unexpected end of file') === false) { - console.error(error.message) - } - - responseDataObj = new Map(Object.entries(testOk)) + // Dashboards API endpoints + const dashboardsMap = new Map() + dashboardsMap.set('status_details', [SystemStatusDashboardApi, 'getSystemStatusDetails']) + dashboardsMap.set('terms_conditions_details', [SystemTermsConditionsDashboardsApi, 'getSystemTermsConditionsDetails']) + dashboardsMap.set('terms_conditions_summary', [SystemTermsConditionsDashboardsApi, 'getSystemTermsConditionsSummary']) + dashboardsMap.set('connectivity_ccsd_details', [SystemConnectivityCCSDDashboardsApi, 'getSystemConnectivityCcsdDetails']) + dashboardsMap.set('connectivity_ccsd_summary', [SystemConnectivityCCSDDashboardsApi, 'getSystemConnectivityCcsdSummary']) + dashboardsMap.set('atc_iatc_details', [SystemATCIATCDashboardApi, 'getSystemAtcIatcDetails']) + dashboardsMap.set('questionnaire_summary', [SystemQuestionnaireDashboardsApi, 'getSystemQuestionnaireSummary']) + dashboardsMap.set('questionnaire_details', [SystemQuestionnaireDashboardsApi, 'getSystemQuestionnaireDetails']) + dashboardsMap.set('workflows_history_summary', [SystemWorkflowsDashboardsApi, 'getSystemWorkflowsHistorySummary']) + dashboardsMap.set('workflows_history_details', [SystemWorkflowsDashboardsApi, 'getSystemWorkflowsHistoryDetails']) + dashboardsMap.set('workflows_history_stage_details', [SystemWorkflowsDashboardsApi, 'getSystemWorkflowsHistoryStageDetails']) + dashboardsMap.set('control_compliance_summary', [SystemSecurityControlsDashboardsApi, 'getSystemControlComplianceSummary']) + dashboardsMap.set('security_control_details', [SystemSecurityControlsDashboardsApi, 'getSystemSecurityControlDetails']) + dashboardsMap.set('assessment_procedures_details', [SystemSecurityControlsDashboardsApi, 'getSystemAssessmentProceduresDetails']) + dashboardsMap.set('poam_summary', [SystemPOAMDashboardsApi, 'getSystemPoamSummary']) + dashboardsMap.set('poam_details', [SystemPOAMDashboardsApi, 'getSystemPoamDetails']) + dashboardsMap.set('artifacts_summary', [SystemArtifactsDashboardsApi, 'getSystemArtifactsSummary']) + dashboardsMap.set('artifacts_details', [SystemArtifactsDashboardsApi, 'getSystemArtifactsDetails']) + dashboardsMap.set('hardware_summary', [SystemHardwareDashboardsApi, 'getSystemHardwareSummary']) + dashboardsMap.set('hardware_details', [SystemHardwareDashboardsApi, 'getSystemHardwareDetails']) + dashboardsMap.set('sensor_hardware_summary', [SystemSensorHardwareDashboardsApi, 'getSystemSensorHardwareSummary']) + dashboardsMap.set('sensor_hardware_details', [SystemSensorHardwareDashboardsApi, 'getSystemSensorHardwareDetails']) + dashboardsMap.set('software_summary', [SystemSoftwareDashboardsApi, 'getSystemSoftwareSummary']) + dashboardsMap.set('software_details', [SystemSoftwareDashboardsApi, 'getSystemSoftwareDetails']) + dashboardsMap.set('sensor_software_summary', [SystemSensorSoftwareDashboardsApi, 'getSystemSensorSoftwareSummary']) + dashboardsMap.set('sensor_software_details', [SystemSensorSoftwareDashboardsApi, 'getSystemSensorSoftwareDetails']) + dashboardsMap.set('sensor_software_counts', [SystemSensorSoftwareDashboardsApi, 'getSystemSensorSoftwareCounts']) + dashboardsMap.set('critical_assets_summary', [SystemCriticalAssetsDashboardApi, 'getSystemCriticalAssetsSummary']) + dashboardsMap.set('vulnerability_summary', [SystemVulnerabilityDashboardApi, 'getSystemVulnerabilitySummary']) + dashboardsMap.set('device_findings_summary', [SystemDeviceFindingsDashboardsApi, 'getSystemDeviceFindingsSummary']) + dashboardsMap.set('device_findings_details', [SystemDeviceFindingsDashboardsApi, 'getSystemDeviceFindingsDetails']) + dashboardsMap.set('application_findings_summary', [SystemApplicationFindingsDashboardsApi, 'getSystemApplicationFindingsSummary']) + dashboardsMap.set('application_findings_details', [SystemApplicationFindingsDashboardsApi, 'getSystemApplicationFindingsDetails']) + dashboardsMap.set('ports_protocols_summary', [SystemPortsProtocolsDashboardsApi, 'getSystemPortsProtocolsSummary']) + dashboardsMap.set('ports_protocols_details', [SystemPortsProtocolsDashboardsApi, 'getSystemPortsProtocolsDetails']) + dashboardsMap.set('integration_status_summary', [SystemCONMONIntegrationStatusDashboardApi, 'getSystemCommonIntegrationStatusSummary']) + dashboardsMap.set('associations_details', [SystemAssociationsDashboardApi, 'getSystemAssociationsDetails']) + dashboardsMap.set('user_assignments_details', [UserSystemAssignmentsDashboardApi, 'getUserSystemAssignmentsDetails']) + dashboardsMap.set('org_migration_status', [OrganizationMigrationStatusDashboardApi, 'getOrganizationMigrationStatusSummary']) + dashboardsMap.set('system_migration_status', [SystemMigrationStatusDashboardApi, 'getSystemMigrationStatusSummary']) + dashboardsMap.set('fisma_metrics', [SystemFISMAMetricsDashboardApi, 'getSystemFismaMetrics']) + dashboardsMap.set('coast_guard_fisma_metrics', [CoastGuardSystemFISMAMetricsDashboardApi, 'getCoastGuardSystemFismaMetrics']) + dashboardsMap.set('privacy_summary', [SystemPrivacyDashboardApi, 'getSystemPrivacySummary']) + dashboardsMap.set('fisma_saop_summary', [VAOMBFISMADashboardApi, 'getVaOmbFsmaSaopSummary']) + dashboardsMap.set('va_icamp_tableau_poam_details', [VASystemDashboardsApi, 'getVaSystemIcampTableauPoamDetails']) + dashboardsMap.set('va_aa_summary', [VASystemDashboardsApi, 'getVaSystemAaSummary']) + dashboardsMap.set('va_a2_summary', [VASystemDashboardsApi, 'getVaSystemA2Summary']) + dashboardsMap.set('va_pl_109_summary', [VASystemDashboardsApi, 'getVaSystemPl109ReportingSummary']) + dashboardsMap.set('va_fisma_inventory_summary', [VASystemDashboardsApi, 'getVaSystemFismaInvetorySummary']) + dashboardsMap.set('va_fisma_inventory_crypto_summary', [VASystemDashboardsApi, 'getVaSystemFismaInvetoryCryptoSummary']) + dashboardsMap.set('va_threat_risk_summary', [VASystemDashboardsApi, 'getVaSystemThreatRiskSummary']) + dashboardsMap.set('va_threat_source_details', [VASystemDashboardsApi, 'getVaSystemThreatSourceDetails']) + dashboardsMap.set('va_threat_architecture_details', [VASystemDashboardsApi, 'getVaSystemThreatArchitectureDetails']) + dashboardsMap.set('cmmc_status_summary', [CMMCAssessmentDashboardsApi, 'getCmmcAssessmentStatusSummary']) + dashboardsMap.set('cmmc_compliance_summary', [CMMCAssessmentDashboardsApi, 'getCmmcAssessmentRequirementsComplianceSummary']) + dashboardsMap.set('cmmc_security_requirements_details', [CMMCAssessmentDashboardsApi, 'getCmmcAssessmentSecurityRequirementsDetails']) + dashboardsMap.set('cmmc_requirement_objectives_details', [CMMCAssessmentDashboardsApi, 'getCmmcAssessmentRequirementObjectivesDetails']) + + for (const [key, values] of dashboardsMap) { + it(`Successfully tested endpoint - Dashboard (${key})`, async () => { + // eslint-disable-next-line no-eval + const DashboardClass = eval(values[0]) + const getDashboard = new DashboardClass(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await getDashboard[values[1]](45, false, 1, 2000).then((response: object) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error.message) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') }) - expect(responseDataObj.get('status')).to.equal(200) - expect(responseDataObj.get('statusText')).to.equal('OK') - }) + } }) diff --git a/test/commands/emasser/post.test.ts b/test/commands/emasser/post.test.ts index 0aa8bad09..2f53d7889 100644 --- a/test/commands/emasser/post.test.ts +++ b/test/commands/emasser/post.test.ts @@ -1,23 +1,29 @@ import {expect} from 'chai' import {InitMockServer} from './mock.server' -import {CACApi, PACApi, CloudResourceResultsApi, ContainerScanResultsApi, - MilestonesApi, POAMApi, RegistrationApi, StaticCodeScansApi, - TestResultsApi} from '@mitre/emass_client' -import {CacResponsePost, PacResponsePost, CloudResourcesResponsePost, - ContainersResponsePost, MilestoneResponsePost, PoamResponsePost, - Register, StaticCodeResponsePost, TestResultsResponsePost} from '@mitre/emass_client/dist/api' - -describe('Test eMASS API CLI (post) commands', () => { - const mocSer = new InitMockServer() +import { + ArtifactsApi, CACApi, PACApi, + CloudResourceResultsApi, ContainerScanResultsApi, + MilestonesApi, POAMApi, RegistrationApi, + StaticCodeScansApi, TestResultsApi, + DeviceScanResultsApi, HardwareBaselineApi, + SoftwareBaselineApi, +} from '@mitre/emass_client' +import { + CacResponsePost, PacResponsePost, CloudResourcesResponsePost, + ContainersResponsePost, MilestoneResponsePost, PoamResponsePostPutDelete, + Register, StaticCodeResponsePost, TestResultsResponsePost, + ArtifactsResponsePutPost, DeviceScanResultsResponsePost, + HwBaselineResponsePostPut, SwBaselineResponsePostPut, +} from '@mitre/emass_client/dist/api' + +describe('Test eMASSer API CLI (POST) commands', () => { + const mocServer = new InitMockServer() let responseDataObj: Map - const testOk = { - status: 200, - statusText: 'OK', - } + const testOk = {status: 200, statusText: 'OK'} - it('Successfully tested endpoint - cac', async () => { - const addCac = new CACApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await addCac.addSystemCac(123, []).then((response: CacResponsePost) => { + it('Successfully tested endpoint - Artifacts', async () => { + const artifactApi = new ArtifactsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await artifactApi.addArtifactsBySystemId(123, 'filename', true, false, 'type', 'category').then((response: ArtifactsResponsePutPost) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -30,9 +36,9 @@ describe('Test eMASS API CLI (post) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - pac', async () => { - const addCac = new PACApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await addCac.addSystemPac(123, []).then((response: PacResponsePost) => { + it('Successfully tested endpoint - CAC', async () => { + const addCac = new CACApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await addCac.addSystemCac(123, []).then((response: CacResponsePost) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -45,8 +51,8 @@ describe('Test eMASS API CLI (post) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - cloud_resources', async () => { - const addCloudResource = new CloudResourceResultsApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Cloud Resources', async () => { + const addCloudResource = new CloudResourceResultsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await addCloudResource.addCloudResourcesBySystemId(123, []).then((response: CloudResourcesResponsePost) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -60,8 +66,8 @@ describe('Test eMASS API CLI (post) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - container_scans', async () => { - const addContainer = new ContainerScanResultsApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Container Scans', async () => { + const addContainer = new ContainerScanResultsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await addContainer.addContainerSansBySystemId(123, []).then((response: ContainersResponsePost) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -75,8 +81,38 @@ describe('Test eMASS API CLI (post) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - milestones', async () => { - const addMilestone = new MilestonesApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Device Scans', async () => { + const addDeviceScans = new DeviceScanResultsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await addDeviceScans.addScanResultsBySystemId(123, 'disaStigViewerCklCklb', 'filename', false).then((response: DeviceScanResultsResponsePost) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') + }) + + it('Successfully tested endpoint - Hardware Baseline', async () => { + const hwBaseline = new HardwareBaselineApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await hwBaseline.addHwBaselineAssets(123, []).then((response: HwBaselineResponsePostPut) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') + }) + + it('Successfully tested endpoint - Milestones', async () => { + const addMilestone = new MilestonesApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await addMilestone.addMilestoneBySystemIdAndPoamId(123, 456, []).then((response: MilestoneResponsePost) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -90,9 +126,24 @@ describe('Test eMASS API CLI (post) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - poams', async () => { - const addPoam = new POAMApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await addPoam.addPoamBySystemId(123, []).then((response: PoamResponsePost) => { + it('Successfully tested endpoint - PAC', async () => { + const addCac = new PACApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await addCac.addSystemPac(123, []).then((response: PacResponsePost) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') + }) + + it('Successfully tested endpoint - POA&Ms', async () => { + const addPoam = new POAMApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await addPoam.addPoamBySystemId(123, []).then((response: PoamResponsePostPutDelete) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { @@ -105,8 +156,8 @@ describe('Test eMASS API CLI (post) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - register', async () => { - const registerAPI = new RegistrationApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Register', async () => { + const registerAPI = new RegistrationApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await registerAPI.registerUser().then((response: Register) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -120,8 +171,23 @@ describe('Test eMASS API CLI (post) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - static_code_scans', async () => { - const addStaticCodeScans = new StaticCodeScansApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Software Baseline', async () => { + const swBaseline = new SoftwareBaselineApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await swBaseline.addSwBaselineAssets(123, []).then((response: SwBaselineResponsePostPut) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') + }) + + it('Successfully tested endpoint - Static Code Scans', async () => { + const addStaticCodeScans = new StaticCodeScansApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await addStaticCodeScans.addStaticCodeScansBySystemId(123, {}).then((response: StaticCodeResponsePost) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -135,8 +201,8 @@ describe('Test eMASS API CLI (post) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - test_results', async () => { - const addTestResults = new TestResultsApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Test Results', async () => { + const addTestResults = new TestResultsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await addTestResults.addTestResultsBySystemId(123, []).then((response: TestResultsResponsePost) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { diff --git a/test/commands/emasser/put.test.ts b/test/commands/emasser/put.test.ts index b221debf5..f93f83e62 100644 --- a/test/commands/emasser/put.test.ts +++ b/test/commands/emasser/put.test.ts @@ -1,19 +1,22 @@ import {expect} from 'chai' import {InitMockServer} from './mock.server' -import {ArtifactsApi, ControlsApi, MilestonesApi, POAMApi} from '@mitre/emass_client' -import {ArtifactsResponsePutPost, ControlsResponsePut, - MilestoneResponsePut, PoamResponsePut} from '@mitre/emass_client/dist/api' +import { + ArtifactsApi, ControlsApi, MilestonesApi, + POAMApi, HardwareBaselineApi, SoftwareBaselineApi, +} from '@mitre/emass_client' +import { + ArtifactsResponsePutPost, ControlsResponsePut, + MilestoneResponsePut, PoamResponsePostPutDelete, + HwBaselineResponsePostPut, SwBaselineResponsePostPut, +} from '@mitre/emass_client/dist/api' -describe('Test eMASS API CLI (put) commands', () => { - const mocSer = new InitMockServer() +describe('Test eMASSer API CLI (PUT) commands', () => { + const mocServer = new InitMockServer() let responseDataObj: Map - const testOk = { - status: 200, - statusText: 'OK', - } + const testOk = {status: 200, statusText: 'OK'} - it('Successfully tested endpoint - artifacts', async () => { - const artifactApi = new ArtifactsApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Artifacts', async () => { + const artifactApi = new ArtifactsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await artifactApi.updateArtifactBySystemId(123, []).then((response: ArtifactsResponsePutPost) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -27,8 +30,8 @@ describe('Test eMASS API CLI (put) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - controls', async () => { - const updateControl = new ControlsApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Controls', async () => { + const updateControl = new ControlsApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await updateControl.updateControlBySystemId(123, []).then((response: ControlsResponsePut) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -42,8 +45,23 @@ describe('Test eMASS API CLI (put) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - milestones', async () => { - const putMilestones = new MilestonesApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) + it('Successfully tested endpoint - Hardware Baseline', async () => { + const hwBaseline = new HardwareBaselineApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await hwBaseline.updateHwBaselineAssets(123, []).then((response: HwBaselineResponsePostPut) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') + }) + + it('Successfully tested endpoint - Milestones', async () => { + const putMilestones = new MilestonesApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) await putMilestones.updateMilestoneBySystemIdAndPoamId(123, 456, []).then((response: MilestoneResponsePut) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { @@ -57,9 +75,24 @@ describe('Test eMASS API CLI (put) commands', () => { expect(responseDataObj.get('statusText')).to.equal('OK') }) - it('Successfully tested endpoint - poams', async () => { - const updatePoam = new POAMApi(mocSer.configuration, mocSer.basePath, mocSer.axiosInstances) - await updatePoam.updatePoamBySystemId(123, []).then((response: PoamResponsePut) => { + it('Successfully tested endpoint - POA&Ms', async () => { + const updatePoam = new POAMApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await updatePoam.updatePoamBySystemId(123, []).then((response: PoamResponsePostPutDelete) => { + responseDataObj = new Map(Object.entries(response)) + }).catch((error:any) => { + if (error.message.includes('unexpected end of file') === false) { + console.error(error) + } + + responseDataObj = new Map(Object.entries(testOk)) + }) + expect(responseDataObj.get('status')).to.equal(200) + expect(responseDataObj.get('statusText')).to.equal('OK') + }) + + it('Successfully tested endpoint - Software Baseline', async () => { + const swBaseline = new SoftwareBaselineApi(mocServer.configuration, mocServer.basePath, mocServer.axiosInstances) + await swBaseline.updateSwBaselineAssets(123, []).then((response: SwBaselineResponsePostPut) => { responseDataObj = new Map(Object.entries(response)) }).catch((error:any) => { if (error.message.includes('unexpected end of file') === false) { diff --git a/test/commands/generate/delta.test.ts b/test/commands/generate/delta.test.ts index df1a7fddd..84dd0d94a 100644 --- a/test/commands/generate/delta.test.ts +++ b/test/commands/generate/delta.test.ts @@ -93,23 +93,23 @@ describe('Test generate delta command', () => { }) // should process delta using the fuzzy logic - it('should generate the correct number of controls using fuzzy logic to match and map controls', async () => { - const {stdout} = await runCommand<{name: string}>(['generate delta', - '-J', path.resolve('./test/sample_data/inspec/json/profile_and_controls/Windows_Server_2022_v1r3_mini-profile.json'), - '-X', path.resolve('./test/sample_data/xccdf/stigs/Windows_Server_2022_V2R1_mini-sample-xccdf.xml'), - '-o', `${tmpobj.name}`, - '-M', - '-c', path.resolve('./test/sample_data/inspec/json/profile_and_controls/windows_server_2022_v1r3_mini_controls/'), - ]) + // it('should generate the correct number of controls using fuzzy logic to match and map controls', async () => { + // const {stdout} = await runCommand<{name: string}>(['generate delta', + // '-J', path.resolve('./test/sample_data/inspec/json/profile_and_controls/Windows_Server_2022_v1r3_mini-profile.json'), + // '-X', path.resolve('./test/sample_data/xccdf/stigs/Windows_Server_2022_V2R1_mini-sample-xccdf.xml'), + // '-o', `${tmpobj.name}`, + // '-M', + // '-c', path.resolve('./test/sample_data/inspec/json/profile_and_controls/windows_server_2022_v1r3_mini_controls/'), + // ]) - const output = stdout.split('\n') - expect(output.includes('Total Controls Found on Delta Directory: 5')) - expect(output.includes('Total Controls Found on XCCDF: 5')) - expect(output.includes('Match Controls: 5')) - expect(output.includes('["+","SV-254238"]')) - expect(output.includes('["+","SV-254239"]')) - expect(output.includes('["+","SV-254240"]')) - expect(output.includes('["+","SV-254241"]')) - expect(output.includes('["+","SV-254242"]')) - }) + // console.log(stdout) + // expect(stdout).to.include('Total Controls Available for Delta: 5') + // expect(stdout).to.include('Total Controls Found on XCCDF: 5') + // expect(stdout).to.include('Match Controls: 5') + // expect(stdout).to.include('Mapping (From --> To): V-93369 --> SV-254238') + // expect(stdout).to.include('Mapping (From --> To): V-93473 --> SV-254239') + // expect(stdout).to.include('Mapping (From --> To): V-93205 --> SV-254240') + // expect(stdout).to.include('Mapping (From --> To): V-93207 --> SV-254241') + // expect(stdout).to.include('Mapping (From --> To): V-93461 --> SV-254242') + // }) }) diff --git a/test/commands/generate/inspec_profile.test.ts b/test/commands/generate/inspec_profile.test.ts index 7a22d188f..5c6bcd5ce 100644 --- a/test/commands/generate/inspec_profile.test.ts +++ b/test/commands/generate/inspec_profile.test.ts @@ -7,13 +7,24 @@ import {processXCCDF} from '@mitre/inspec-objects' describe('Test inspec_profile (aliases:xccdf_benchmark2inspec)', () => { const tmpobj = tmp.dirSync({unsafeCleanup: true}) + // Remove all controlled temporary objects on process exit + tmp.setGracefulCleanup() fs.readdirSync('./test/sample_data/xccdf/stigs').forEach(file => { - it(`Has the same number of controls in the stig as generated - ${file}`, async () => { + it(`Generated scaffold has the same number of controls based on STIG benchmark: ${file}`, async () => { await runCommand<{name: string}>(['generate inspec_profile', '-X', path.resolve('./test/sample_data/xccdf/stigs', file), '-o', `${tmpobj.name}/${file}`]) const parsedXCCDF = processXCCDF(fs.readFileSync(path.resolve('./test/sample_data/xccdf/stigs', file), 'utf8'), false, 'rule') const fileCount = fs.readdirSync(`${tmpobj.name}/${file}/controls/`).length expect(fileCount).to.eql(parsedXCCDF.controls.length) }) }) + + fs.readdirSync('./test/sample_data/xccdf/cis').forEach(file => { + it(`Generated scaffold has the same number of controls based on CIS benchmark: ${file}`, async () => { + await runCommand<{name: string}>(['generate inspec_profile', '-X', path.resolve('./test/sample_data/xccdf/cis', file), '-T', 'cis', '-o', `${tmpobj.name}/${file}`]) + const parsedXCCDF = processXCCDF(fs.readFileSync(path.resolve('./test/sample_data/xccdf/cis', file), 'utf8'), false, 'rule') + const fileCount = fs.readdirSync(`${tmpobj.name}/${file}/controls/`).length + expect(fileCount).to.eql(parsedXCCDF.controls.length) + }) + }) }) diff --git a/test/sample_data/HDF/input/triple_overlay_profile_sample.json b/test/sample_data/HDF/input/triple_overlay_profile_sample.json new file mode 100644 index 000000000..10c15bd52 --- /dev/null +++ b/test/sample_data/HDF/input/triple_overlay_profile_sample.json @@ -0,0 +1,1247 @@ +{ + "platform": { + "name": "centos", + "release": "7.7.1908" + }, + "profiles": [ + { + "name": "cms-ars-3.1-moderate-aws-rds-oracle-database-12c-stig-overlay", + "version": "0.1.0", + "sha256": "3fe40f9476a23b5b4dd6c0da2bb8dbe8ca5a4a8b6bfb27ffbf9f1797160c0f91", + "title": ".", + "maintainer": "CMS InSpec Dev Team", + "summary": ".", + "license": "Apache-2.0", + "copyright": ".", + "supports": [], + "attributes": [ + { + "name": "user", + "options": { + "value": "admin1" + } + }, + { + "name": "password", + "options": { + "value": "" + } + }, + { + "name": "host", + "options": { + "value": "oracle.host" + } + }, + { + "name": "service", + "options": { + "value": "ORCL" + } + }, + { + "name": "sqlplus_bin", + "options": { + "value": "/usr/bin/sqlplus" + } + }, + { + "name": "standard_auditing_used", + "options": { + "type": "Boolean", + "value": false + } + }, + { + "name": "unified_auditing_used", + "options": { + "type": "Boolean", + "value": false + } + }, + { + "name": "allowed_db_links", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_dbadmin_users", + "options": { + "value": [] + } + }, + { + "name": "users_allowed_access_to_public", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_dba_role", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_system_tablespace", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_application_owners", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_unlocked_oracledb_accounts", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "users_allowed_access_to_dictionary_table", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_with_admin_privs", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_audit_users", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_dbaobject_owners", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_oracledb_components", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_oracledb_components_integrated_into_dbms", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "oracle_dbas", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "emergency_profile_list", + "options": { + "type": "Array", + "value": [ + "RDSADMIN" + ] + } + } + ], + "depends": [ + { + "name": "aws-rds-oracle-database-12c-stig-baseline", + "path": "../aws-rds-oracle-database-12c-stig-baseline", + "status": "loaded" + } + ], + "groups": [ + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61409.rb", + "controls": [ + "V-61409" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61449.rb", + "controls": [ + "V-61449" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61867.rb", + "controls": [ + "V-61867" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61635.rb", + "controls": [ + "V-61635" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61677.rb", + "controls": [ + "V-61677" + ] + } + ], + "controls": [ + { + "id": "V-61409", + "title": "Audit trail data must be retained online for ninety (90) days and archived \n for old records for one (1) year.", + "desc": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding.", + "descriptions": [ + { + "label": "default", + "data": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding." + }, + { + "label": "check", + "data": "Develop, document and implement an audit retention policy and \n procedures.\n\n It is recommended that the most recent ninety days of audit logs remain \n available online.\n\n After thirty ninety days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to \n investigate suspicious activity." + } + ], + "impact": 0.5, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61409", + "rid": "SV-75899r1_rule", + "stig_id": "O121-BP-021100", + "fix_id": "F-67325r1_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review and verify the implementation of an audit trail\n retention policy.\n\n Verify that audit data is maintained for a minimum of one year.\n\n If audit data is not maintained for a minimum of one year, this is a finding.", + "fix": "Develop, document and implement an audit retention policy and\n procedures.\n\n It is recommended that the most recent thirty days of audit logs remain\n available online.\n\n After thirty days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to\n investigate suspicious activity." + }, + "code": " control 'V-61409' do\n title 'Audit trail data must be retained online for ninety (90) days and archived \n for old records for one (1) year.'\n desc 'Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding.'\n desc 'check', 'Develop, document and implement an audit retention policy and \n procedures.\n\n It is recommended that the most recent ninety days of audit logs remain \n available online.\n\n After thirty ninety days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to \n investigate suspicious activity.'\n end\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61409.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61449", + "title": "Database job/batch queues must be reviewed regularly to detect\n unauthorized database job submissions.", + "desc": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions.", + "descriptions": [ + { + "label": "default", + "data": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61449", + "rid": "SV-75939r3_rule", + "stig_id": "O121-BP-023100", + "fix_id": "F-67365r2_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "The DBMS_JOB PL/SQL package has been replaced by DBMS_SCHEDULER\n in Oracle versions 10.1 and higher, though it continues to be supported for\n backward compatibility.\n\n Run this query:\n select value from v$parameter where name = 'job_queue_processes';\n\n Run this query:\n select value from all_scheduler_global_attribute\n where ATTRIBUTE_NAME = 'MAX_JOB_SLAVE_PROCESSES';\n\n To understand the relationship between these settings, review:\n https://docs.oracle.com/database/121/ADMIN/appendix_a.htm#ADMIN11002\n\n Review documented and implemented procedures for monitoring the Oracle DBMS\n job/batch queues for unauthorized submissions. If procedures for job queue\n review are not defined, documented or evidence of implementation does not\n exist, this is a finding.\n\n Job queue information is available from the DBA_JOBS view. The following\n command lists jobs submitted to the queue. DBMS_JOB does not generate a\n 'history' of previous job executions.\n\n Run this query:\n select job, next_date, next_sec, failures, broken from dba_jobs;\n\n Scheduler queue information is available from the DBA_SCHEDULER_JOBS view. The\n following command lists jobs submitted to the queue.\n\n Run this query:\n select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;", + "fix": "Develop, document and implement procedures to monitor the\n database job queues for unauthorized job submissions.\n\n Develop, document and implement a formal migration plan to convert jobs using\n DBMS_JOB to use DBMS_SCHEDULER instead for Oracle versions 10.1 and higher.\n (This does not apply to DBMS_JOB jobs generated by Oracle itself, such as those\n for refreshing materialized views.)\n\n Set the value of the job_queue_processes parameter to a low value to restrict\n concurrent DBMS_JOB executions.\n\n Use auditing to capture use of the DBMS_JOB package in the audit trail. Review\n the audit trail for unauthorized use of the DBMS_JOB package." + }, + "code": "control 'V-61449' do\n title \"Database job/batch queues must be reviewed regularly to detect\n unauthorized database job submissions.\"\n desc \"Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions.\"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000516-DB-999900'\n tag \"gid\": 'V-61449'\n tag \"rid\": 'SV-75939r3_rule'\n tag \"stig_id\": 'O121-BP-023100'\n tag \"fix_id\": 'F-67365r2_fix'\n tag \"cci\": ['CCI-000366']\n tag \"nist\": ['CM-6 b', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"The DBMS_JOB PL/SQL package has been replaced by DBMS_SCHEDULER\n in Oracle versions 10.1 and higher, though it continues to be supported for\n backward compatibility.\n\n Run this query:\n select value from v$parameter where name = 'job_queue_processes';\n\n Run this query:\n select value from all_scheduler_global_attribute\n where ATTRIBUTE_NAME = 'MAX_JOB_SLAVE_PROCESSES';\n\n To understand the relationship between these settings, review:\n https://docs.oracle.com/database/121/ADMIN/appendix_a.htm#ADMIN11002\n\n Review documented and implemented procedures for monitoring the Oracle DBMS\n job/batch queues for unauthorized submissions. If procedures for job queue\n review are not defined, documented or evidence of implementation does not\n exist, this is a finding.\n\n Job queue information is available from the DBA_JOBS view. The following\n command lists jobs submitted to the queue. DBMS_JOB does not generate a\n 'history' of previous job executions.\n\n Run this query:\n select job, next_date, next_sec, failures, broken from dba_jobs;\n\n Scheduler queue information is available from the DBA_SCHEDULER_JOBS view. The\n following command lists jobs submitted to the queue.\n\n Run this query:\n select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;\"\n tag \"fix\": \"Develop, document and implement procedures to monitor the\n database job queues for unauthorized job submissions.\n\n Develop, document and implement a formal migration plan to convert jobs using\n DBMS_JOB to use DBMS_SCHEDULER instead for Oracle versions 10.1 and higher.\n (This does not apply to DBMS_JOB jobs generated by Oracle itself, such as those\n for refreshing materialized views.)\n\n Set the value of the job_queue_processes parameter to a low value to restrict\n concurrent DBMS_JOB executions.\n\n Use auditing to capture use of the DBMS_JOB package in the audit trail. Review\n the audit trail for unauthorized use of the DBMS_JOB package.\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n database_jobs = sql.query(\"select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;\").column('job_name')\n\n describe \"You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: #{database_jobs}\" do\n skip \"You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: #{database_jobs}\"\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61449.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61635", + "title": "The DBMS must produce audit records containing sufficient information\n to establish the sources (origins) of the events.", + "desc": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable.", + "descriptions": [ + { + "label": "default", + "data": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000098-DB-000042", + "gid": "V-61635", + "rid": "SV-76125r1_rule", + "stig_id": "O121-C2-007700", + "fix_id": "F-67547r1_fix", + "cci": [ + "CCI-000133" + ], + "nist": [ + "AU-3", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Verify, using vendor and system documentation if necessary,\n that the DBMS is configured to use Oracle's auditing features, or that a\n third-party product or custom code is deployed and configured to satisfy this\n requirement.\n\n If a third-party product or custom code is used, compare its current\n configuration with the audit requirements. If any of the requirements is not\n covered by the configuration, this is a finding.\n\n The remainder of this Check is applicable specifically where Oracle auditing is\n in use.\n\n If Standard Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SHOW PARAMETER AUDIT_TRAIL\n\n or the following SQL query:\n\n SELECT * FROM SYS.V$PARAMETER WHERE NAME = 'audit_trail';\n\n If Oracle returns the value 'NONE', this is a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the SYS.AUD$\n table or the audit file, whichever is in use.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\n\n If Unified Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SELECT * FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\n\n If Oracle returns the value \"TRUE\", this is not a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the\n SYS.UNIFIED_AUDIT_TRAIL view.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.", + "fix": "Configure the DBMS's auditing to audit standard and\n organization-defined auditable events, the audit record to include the source\n of the event. If preferred, use a third-party or custom tool.\n\n If using a third-party product, proceed in accordance with the product\n documentation. If using Oracle's capabilities, proceed as follows.\n\n If Standard Auditing is used:\n Use this process to ensure auditable events are captured:\n\n ALTER SYSTEM SET AUDIT_TRAIL= SCOPE=SPFILE;\n\n Audit trail type can be 'OS', 'DB', 'DB,EXTENDED', 'XML' or 'XML,EXTENDED'.\n After executing this statement, it may be necessary to shut down and restart\n the Oracle database.\n\n If Unified Auditing is used:\n To ensure auditable events are captured:\n Link the oracle binary with uniaud_on, and then restart the database.\n\n\n\n Oracle Database Upgrade Guide describes how to enable unified auditing.\n\n For more information on the configuration of auditing, refer to the following\n documents:\n \"Auditing Database Activity\" in the Oracle Database 2 Day + Security Guide:\n http://docs.oracle.com/database/121/TDPSG/tdpsg_auditing.htm#TDPSG50000\n \"Monitoring Database Activity with Auditing\" in the Oracle Database Security\n Guide:\n http://docs.oracle.com/database/121/DBSEG/part_6.htm#CCHEHCGI\n \"DBMS_AUDIT_MGMT\" in the Oracle Database PL/SQL Packages and Types Reference:\n http://docs.oracle.com/database/121/ARPLS/d_audit_mgmt.htm#ARPLS241\n Oracle Database Upgrade Guide:\n http://docs.oracle.com/database/121/UPGRD/afterup.htm#UPGRD52810" + }, + "code": "control 'V-61635' do\n title \"The DBMS must produce audit records containing sufficient information\n to establish the sources (origins) of the events.\"\n desc \"Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000098-DB-000042'\n tag \"gid\": 'V-61635'\n tag \"rid\": 'SV-76125r1_rule'\n tag \"stig_id\": 'O121-C2-007700'\n tag \"fix_id\": 'F-67547r1_fix'\n tag \"cci\": ['CCI-000133']\n tag \"nist\": ['AU-3', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"Verify, using vendor and system documentation if necessary,\n that the DBMS is configured to use Oracle's auditing features, or that a\n third-party product or custom code is deployed and configured to satisfy this\n requirement.\n\n If a third-party product or custom code is used, compare its current\n configuration with the audit requirements. If any of the requirements is not\n covered by the configuration, this is a finding.\n\n The remainder of this Check is applicable specifically where Oracle auditing is\n in use.\n\n If Standard Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SHOW PARAMETER AUDIT_TRAIL\n\n or the following SQL query:\n\n SELECT * FROM SYS.V$PARAMETER WHERE NAME = 'audit_trail';\n\n If Oracle returns the value 'NONE', this is a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the SYS.AUD$\n table or the audit file, whichever is in use.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\n\n If Unified Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SELECT * FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\n\n If Oracle returns the value \\\"TRUE\\\", this is not a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the\n SYS.UNIFIED_AUDIT_TRAIL view.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\"\n tag \"fix\": \"Configure the DBMS's auditing to audit standard and\n organization-defined auditable events, the audit record to include the source\n of the event. If preferred, use a third-party or custom tool.\n\n If using a third-party product, proceed in accordance with the product\n documentation. If using Oracle's capabilities, proceed as follows.\n\n If Standard Auditing is used:\n Use this process to ensure auditable events are captured:\n\n ALTER SYSTEM SET AUDIT_TRAIL= SCOPE=SPFILE;\n\n Audit trail type can be 'OS', 'DB', 'DB,EXTENDED', 'XML' or 'XML,EXTENDED'.\n After executing this statement, it may be necessary to shut down and restart\n the Oracle database.\n\n If Unified Auditing is used:\n To ensure auditable events are captured:\n Link the oracle binary with uniaud_on, and then restart the database.\n\n\n\n Oracle Database Upgrade Guide describes how to enable unified auditing.\n\n For more information on the configuration of auditing, refer to the following\n documents:\n \\\"Auditing Database Activity\\\" in the Oracle Database 2 Day + Security Guide:\n http://docs.oracle.com/database/121/TDPSG/tdpsg_auditing.htm#TDPSG50000\n \\\"Monitoring Database Activity with Auditing\\\" in the Oracle Database Security\n Guide:\n http://docs.oracle.com/database/121/DBSEG/part_6.htm#CCHEHCGI\n \\\"DBMS_AUDIT_MGMT\\\" in the Oracle Database PL/SQL Packages and Types Reference:\n http://docs.oracle.com/database/121/ARPLS/d_audit_mgmt.htm#ARPLS241\n Oracle Database Upgrade Guide:\n http://docs.oracle.com/database/121/UPGRD/afterup.htm#UPGRD52810\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n standard_auditing_used = input('standard_auditing_used')\n unified_auditing_used = input('unified_auditing_used')\n\n describe.one do\n describe 'Standard auditing is in use for audit purposes' do\n subject { standard_auditing_used }\n it { should be true }\n end\n\n describe 'Unified auditing is in use for audit purposes' do\n subject { unified_auditing_used }\n it { should be true }\n end\n end\n\n audit_trail = sql.query(\"select value from v$parameter where name = 'audit_trail';\").column('value')\n audit_info_captured = sql.query('SELECT * FROM UNIFIED_AUDIT_TRAIL;').column('EVENT_TIMESTAMP')\n\n if standard_auditing_used\n describe 'The oracle database audit_trail parameter' do\n subject { audit_trail }\n it { should_not cmp 'NONE' }\n end\n end\n\n unified_auditing = sql.query(\"SELECT value FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\").column('value')\n\n if unified_auditing_used\n describe 'The oracle database unified auditing parameter' do\n subject { unified_auditing }\n it { should_not cmp 'FALSE' }\n end\n\n describe 'The oracle database unified auditing events captured' do\n subject { audit_info_captured }\n it { should_not be_empty }\n end\n\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61635.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61867", + "title": "Database software, applications, and configuration files must be\n monitored to discover unauthorized changes.", + "desc": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations.", + "descriptions": [ + { + "label": "default", + "data": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations." + } + ], + "impact": 0.0, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000133-DB-000179", + "gid": "V-61867", + "rid": "SV-76357r2_rule", + "stig_id": "O121-OS-010700", + "fix_id": "F-67783r2_fix", + "cci": [ + "CCI-001499" + ], + "nist": [ + "CM-5 (6)", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review monitoring procedures and implementation evidence to\n verify that monitoring of changes to database software libraries, related\n applications, and configuration files is done.\n\n Verify that the list of files and directories being monitored is complete. If\n monitoring does not occur or is not complete, this is a finding.", + "fix": "Implement procedures to monitor for unauthorized changes to DBMS\n software libraries, related software application libraries, and configuration\n files. If a third-party automated tool is not employed, an automated job that\n reports file information on the directories and files of interest and compares\n them to the baseline report for the same will meet the requirement.\n\n File hashes or checksums should be used for comparisons since file dates may be\n manipulated by malicious users." + }, + "code": "control 'V-61867' do\n title \"Database software, applications, and configuration files must be\n monitored to discover unauthorized changes.\"\n desc \"Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000133-DB-000179'\n tag \"gid\": 'V-61867'\n tag \"rid\": 'SV-76357r2_rule'\n tag \"stig_id\": 'O121-OS-010700'\n tag \"fix_id\": 'F-67783r2_fix'\n tag \"cci\": ['CCI-001499']\n tag \"nist\": ['CM-5 (6)', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"Review monitoring procedures and implementation evidence to\n verify that monitoring of changes to database software libraries, related\n applications, and configuration files is done.\n\n Verify that the list of files and directories being monitored is complete. If\n monitoring does not occur or is not complete, this is a finding.\"\n tag \"fix\": \"Implement procedures to monitor for unauthorized changes to DBMS\n software libraries, related software application libraries, and configuration\n files. If a third-party automated tool is not employed, an automated job that\n reports file information on the directories and files of interest and compares\n them to the baseline report for the same will meet the requirement.\n\n File hashes or checksums should be used for comparisons since file dates may be\n manipulated by malicious users.\"\n describe command('grep aide /etc/crontab /etc/cron.*/*') do\n its('stdout.strip') { should_not be_empty }\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61867.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61677", + "title": "Default demonstration and sample databases, database objects, and\n applications must be removed.", + "desc": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system.", + "descriptions": [ + { + "label": "default", + "data": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000141-DB-000090", + "gid": "V-61677", + "rid": "SV-76167r3_rule", + "stig_id": "O121-C2-011500", + "fix_id": "F-67591r1_fix", + "cci": [ + "CCI-000381" + ], + "nist": [ + "CM-7 a", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "If Oracle is hosted on a server that does not support\n production systems, and is designated for the deployment of samples and\n demonstrations, this is not applicable (NA).\n\n Review documentation and websites from Oracle and any other relevant vendors\n for vendor-provided demonstration or sample databases, database applications,\n schemas, objects, and files.\n\n Review the Oracle DBMS to determine if any of the demonstration and sample\n databases, schemas, database applications, or files are installed in the\n database or are included with the DBMS application. If any are present in the\n database or are included with the DBMS application, this is a finding.\n\n The Oracle Default Sample Schema User Accounts are:\n\n BI\n Owns the Business Intelligence schema included in the Oracle Sample Schemas.\n\n HR\n Manages the Human Resources schema. Schema stores information about the\n employees and the facilities of the company.\n\n OE\n Manages the Order Entry schema. Schema stores product inventories and sales of\n the company's products through various channels.\n\n PM\n Manages the Product Media schema. Schema contains descriptions and detailed\n information about each product sold by the company.\n\n IX\n Manages the Information Exchange schema. Schema manages shipping through\n business-to-business (B2B) applications database.\n\n SH\n Manages the Sales schema. Schema stores statistics to facilitate business\n decisions.\n\n SCOTT\n A demonstration account with a simple schema.\n\n Connect to Oracle as SYSDBA; run the following SQL to check for presence of\n Oracle Default Sample Schema User Accounts:\n select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\n\n If any of the users listed above is returned it means that there are demo\n programs installed, so this is a finding.\n ", + "fix": "Remove any demonstration and sample databases, database\n applications, objects, and files from the DBMS.\n\n To remove an account and all objects owned by that account (using BI as an\n example):\n DROP USER BI CASCADE;\n\n To remove objects without removing their owner, use the appropriate DROP\n statement (DROP TABLE, DROP VIEW, etc.)." + }, + "code": "control 'V-61677' do\n title \"Default demonstration and sample databases, database objects, and\n applications must be removed.\"\n desc \"Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000141-DB-000090'\n tag \"gid\": 'V-61677'\n tag \"rid\": 'SV-76167r3_rule'\n tag \"stig_id\": 'O121-C2-011500'\n tag \"fix_id\": 'F-67591r1_fix'\n tag \"cci\": ['CCI-000381']\n tag \"nist\": ['CM-7 a', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"If Oracle is hosted on a server that does not support\n production systems, and is designated for the deployment of samples and\n demonstrations, this is not applicable (NA).\n\n Review documentation and websites from Oracle and any other relevant vendors\n for vendor-provided demonstration or sample databases, database applications,\n schemas, objects, and files.\n\n Review the Oracle DBMS to determine if any of the demonstration and sample\n databases, schemas, database applications, or files are installed in the\n database or are included with the DBMS application. If any are present in the\n database or are included with the DBMS application, this is a finding.\n\n The Oracle Default Sample Schema User Accounts are:\n\n BI\n Owns the Business Intelligence schema included in the Oracle Sample Schemas.\n\n HR\n Manages the Human Resources schema. Schema stores information about the\n employees and the facilities of the company.\n\n OE\n Manages the Order Entry schema. Schema stores product inventories and sales of\n the company's products through various channels.\n\n PM\n Manages the Product Media schema. Schema contains descriptions and detailed\n information about each product sold by the company.\n\n IX\n Manages the Information Exchange schema. Schema manages shipping through\n business-to-business (B2B) applications database.\n\n SH\n Manages the Sales schema. Schema stores statistics to facilitate business\n decisions.\n\n SCOTT\n A demonstration account with a simple schema.\n\n Connect to Oracle as SYSDBA; run the following SQL to check for presence of\n Oracle Default Sample Schema User Accounts:\n select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\n\n If any of the users listed above is returned it means that there are demo\n programs installed, so this is a finding.\n \"\n tag \"fix\": \"Remove any demonstration and sample databases, database\n applications, objects, and files from the DBMS.\n\n To remove an account and all objects owned by that account (using BI as an\n example):\n DROP USER BI CASCADE;\n\n To remove objects without removing their owner, use the appropriate DROP\n statement (DROP TABLE, DROP VIEW, etc.).\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n sample_schema_user_accounts = sql.query(\"select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\").column('username')\n\n describe 'The list of oracle default sample schema user accounts' do\n subject { sample_schema_user_accounts }\n it { should be_empty }\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61677.rb" + }, + "waiver_data": {}, + "results": [] + } + ], + "status": "loaded" + }, + { + "name": "aws-rds-oracle-database-12c-stig-baseline", + "version": "0.1.0", + "sha256": "a34d4b2bb6d5675173abdb1df727cc552807b5c80c1d5de027b85c640f8a0fee", + "title": "InSpec Profile", + "maintainer": "The Authors", + "summary": "An InSpec Compliance Profile", + "license": "Apache-2.0", + "copyright": "The Authors", + "copyright_email": "you@example.com", + "supports": [], + "attributes": [ + { + "name": "user", + "options": { + "value": "admin1" + } + }, + { + "name": "password", + "options": { + "value": "1qaz!QAZ1qaz!QAZ" + } + }, + { + "name": "host", + "options": { + "value": "orcl.ctp9kse964go.us-east-1.rds.amazonaws.com" + } + }, + { + "name": "service", + "options": { + "value": "ORCL" + } + }, + { + "name": "sqlplus_bin", + "options": { + "value": "/usr/bin/sqlplus" + } + }, + { + "name": "standard_auditing_used", + "options": { + "type": "Boolean", + "value": true + } + }, + { + "name": "unified_auditing_used", + "options": { + "type": "Boolean", + "value": false + } + }, + { + "name": "allowed_db_links", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_dbadmin_users", + "options": { + "value": [] + } + }, + { + "name": "users_allowed_access_to_public", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_dba_role", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_system_tablespace", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_application_owners", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_unlocked_oracledb_accounts", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "users_allowed_access_to_dictionary_table", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_with_admin_privs", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_audit_users", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_dbaobject_owners", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_oracledb_components", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_oracledb_components_integrated_into_dbms", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "oracle_dbas", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "emergency_profile_list", + "options": { + "value": [ + "RDSADMIN" + ] + } + } + ], + "parent_profile": "cms-ars-3.1-moderate-aws-rds-oracle-database-12c-stig-overlay", + "depends": [ + { + "name": "oracle-database-12c-stig-baseline", + "git": "https://github.com/mitre/oracle-database-12c-stig-baseline", + "branch": "issue-1112", + "status": "loaded" + } + ], + "groups": [ + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61409.rb", + "controls": [ + "V-61409" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61449.rb", + "controls": [ + "V-61449" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61867.rb", + "controls": [ + "V-61867" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61635.rb", + "controls": [ + "V-61635" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61677.rb", + "controls": [ + "V-61677" + ] + } + ], + "controls": [ + { + "id": "V-61409", + "title": "Audit trail data must be retained online for ninety (90) days and archived \n for old records for one (1) year.", + "desc": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding.", + "descriptions": [ + { + "label": "default", + "data": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding." + }, + { + "label": "check", + "data": "Develop, document and implement an audit retention policy and \n procedures.\n\n It is recommended that the most recent ninety days of audit logs remain \n available online.\n\n After thirty ninety days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to \n investigate suspicious activity." + } + ], + "impact": 0.5, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61409", + "rid": "SV-75899r1_rule", + "stig_id": "O121-BP-021100", + "fix_id": "F-67325r1_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review and verify the implementation of an audit trail\n retention policy.\n\n Verify that audit data is maintained for a minimum of one year.\n\n If audit data is not maintained for a minimum of one year, this is a finding.", + "fix": "Develop, document and implement an audit retention policy and\n procedures.\n\n It is recommended that the most recent thirty days of audit logs remain\n available online.\n\n After thirty days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to\n investigate suspicious activity." + }, + "code": "", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61409.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61449", + "title": "Database job/batch queues must be reviewed regularly to detect\n unauthorized database job submissions.", + "desc": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions.", + "descriptions": [ + { + "label": "default", + "data": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61449", + "rid": "SV-75939r3_rule", + "stig_id": "O121-BP-023100", + "fix_id": "F-67365r2_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "The DBMS_JOB PL/SQL package has been replaced by DBMS_SCHEDULER\n in Oracle versions 10.1 and higher, though it continues to be supported for\n backward compatibility.\n\n Run this query:\n select value from v$parameter where name = 'job_queue_processes';\n\n Run this query:\n select value from all_scheduler_global_attribute\n where ATTRIBUTE_NAME = 'MAX_JOB_SLAVE_PROCESSES';\n\n To understand the relationship between these settings, review:\n https://docs.oracle.com/database/121/ADMIN/appendix_a.htm#ADMIN11002\n\n Review documented and implemented procedures for monitoring the Oracle DBMS\n job/batch queues for unauthorized submissions. If procedures for job queue\n review are not defined, documented or evidence of implementation does not\n exist, this is a finding.\n\n Job queue information is available from the DBA_JOBS view. The following\n command lists jobs submitted to the queue. DBMS_JOB does not generate a\n 'history' of previous job executions.\n\n Run this query:\n select job, next_date, next_sec, failures, broken from dba_jobs;\n\n Scheduler queue information is available from the DBA_SCHEDULER_JOBS view. The\n following command lists jobs submitted to the queue.\n\n Run this query:\n select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;", + "fix": "Develop, document and implement procedures to monitor the\n database job queues for unauthorized job submissions.\n\n Develop, document and implement a formal migration plan to convert jobs using\n DBMS_JOB to use DBMS_SCHEDULER instead for Oracle versions 10.1 and higher.\n (This does not apply to DBMS_JOB jobs generated by Oracle itself, such as those\n for refreshing materialized views.)\n\n Set the value of the job_queue_processes parameter to a low value to restrict\n concurrent DBMS_JOB executions.\n\n Use auditing to capture use of the DBMS_JOB package in the audit trail. Review\n the audit trail for unauthorized use of the DBMS_JOB package." + }, + "code": "", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61449.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61867", + "title": "Database software, applications, and configuration files must be\n monitored to discover unauthorized changes.", + "desc": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations.", + "descriptions": [ + { + "label": "default", + "data": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations." + } + ], + "impact": 0.0, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000133-DB-000179", + "gid": "V-61867", + "rid": "SV-76357r2_rule", + "stig_id": "O121-OS-010700", + "fix_id": "F-67783r2_fix", + "cci": [ + "CCI-001499" + ], + "nist": [ + "CM-5 (6)", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review monitoring procedures and implementation evidence to\n verify that monitoring of changes to database software libraries, related\n applications, and configuration files is done.\n\n Verify that the list of files and directories being monitored is complete. If\n monitoring does not occur or is not complete, this is a finding.", + "fix": "Implement procedures to monitor for unauthorized changes to DBMS\n software libraries, related software application libraries, and configuration\n files. If a third-party automated tool is not employed, an automated job that\n reports file information on the directories and files of interest and compares\n them to the baseline report for the same will meet the requirement.\n\n File hashes or checksums should be used for comparisons since file dates may be\n manipulated by malicious users." + }, + "code": " control 'V-61867' do\n impact 0.0\n describe 'This control is not applicable on oracle within aws rds, as aws manages the operating system in which the oracle database is running on' do\n skip 'This control is not applicable on oracle within aws rds, as aws manages the operating system in which the oracle database is running on'\n end\n end\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61867.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61635", + "title": "The DBMS must produce audit records containing sufficient information\n to establish the sources (origins) of the events.", + "desc": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable.", + "descriptions": [ + { + "label": "default", + "data": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000098-DB-000042", + "gid": "V-61635", + "rid": "SV-76125r1_rule", + "stig_id": "O121-C2-007700", + "fix_id": "F-67547r1_fix", + "cci": [ + "CCI-000133" + ], + "nist": [ + "AU-3", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Verify, using vendor and system documentation if necessary,\n that the DBMS is configured to use Oracle's auditing features, or that a\n third-party product or custom code is deployed and configured to satisfy this\n requirement.\n\n If a third-party product or custom code is used, compare its current\n configuration with the audit requirements. If any of the requirements is not\n covered by the configuration, this is a finding.\n\n The remainder of this Check is applicable specifically where Oracle auditing is\n in use.\n\n If Standard Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SHOW PARAMETER AUDIT_TRAIL\n\n or the following SQL query:\n\n SELECT * FROM SYS.V$PARAMETER WHERE NAME = 'audit_trail';\n\n If Oracle returns the value 'NONE', this is a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the SYS.AUD$\n table or the audit file, whichever is in use.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\n\n If Unified Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SELECT * FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\n\n If Oracle returns the value \"TRUE\", this is not a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the\n SYS.UNIFIED_AUDIT_TRAIL view.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.", + "fix": "Configure the DBMS's auditing to audit standard and\n organization-defined auditable events, the audit record to include the source\n of the event. If preferred, use a third-party or custom tool.\n\n If using a third-party product, proceed in accordance with the product\n documentation. If using Oracle's capabilities, proceed as follows.\n\n If Standard Auditing is used:\n Use this process to ensure auditable events are captured:\n\n ALTER SYSTEM SET AUDIT_TRAIL= SCOPE=SPFILE;\n\n Audit trail type can be 'OS', 'DB', 'DB,EXTENDED', 'XML' or 'XML,EXTENDED'.\n After executing this statement, it may be necessary to shut down and restart\n the Oracle database.\n\n If Unified Auditing is used:\n To ensure auditable events are captured:\n Link the oracle binary with uniaud_on, and then restart the database.\n\n\n\n Oracle Database Upgrade Guide describes how to enable unified auditing.\n\n For more information on the configuration of auditing, refer to the following\n documents:\n \"Auditing Database Activity\" in the Oracle Database 2 Day + Security Guide:\n http://docs.oracle.com/database/121/TDPSG/tdpsg_auditing.htm#TDPSG50000\n \"Monitoring Database Activity with Auditing\" in the Oracle Database Security\n Guide:\n http://docs.oracle.com/database/121/DBSEG/part_6.htm#CCHEHCGI\n \"DBMS_AUDIT_MGMT\" in the Oracle Database PL/SQL Packages and Types Reference:\n http://docs.oracle.com/database/121/ARPLS/d_audit_mgmt.htm#ARPLS241\n Oracle Database Upgrade Guide:\n http://docs.oracle.com/database/121/UPGRD/afterup.htm#UPGRD52810" + }, + "code": "", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61635.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61677", + "title": "Default demonstration and sample databases, database objects, and\n applications must be removed.", + "desc": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system.", + "descriptions": [ + { + "label": "default", + "data": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000141-DB-000090", + "gid": "V-61677", + "rid": "SV-76167r3_rule", + "stig_id": "O121-C2-011500", + "fix_id": "F-67591r1_fix", + "cci": [ + "CCI-000381" + ], + "nist": [ + "CM-7 a", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "If Oracle is hosted on a server that does not support\n production systems, and is designated for the deployment of samples and\n demonstrations, this is not applicable (NA).\n\n Review documentation and websites from Oracle and any other relevant vendors\n for vendor-provided demonstration or sample databases, database applications,\n schemas, objects, and files.\n\n Review the Oracle DBMS to determine if any of the demonstration and sample\n databases, schemas, database applications, or files are installed in the\n database or are included with the DBMS application. If any are present in the\n database or are included with the DBMS application, this is a finding.\n\n The Oracle Default Sample Schema User Accounts are:\n\n BI\n Owns the Business Intelligence schema included in the Oracle Sample Schemas.\n\n HR\n Manages the Human Resources schema. Schema stores information about the\n employees and the facilities of the company.\n\n OE\n Manages the Order Entry schema. Schema stores product inventories and sales of\n the company's products through various channels.\n\n PM\n Manages the Product Media schema. Schema contains descriptions and detailed\n information about each product sold by the company.\n\n IX\n Manages the Information Exchange schema. Schema manages shipping through\n business-to-business (B2B) applications database.\n\n SH\n Manages the Sales schema. Schema stores statistics to facilitate business\n decisions.\n\n SCOTT\n A demonstration account with a simple schema.\n\n Connect to Oracle as SYSDBA; run the following SQL to check for presence of\n Oracle Default Sample Schema User Accounts:\n select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\n\n If any of the users listed above is returned it means that there are demo\n programs installed, so this is a finding.\n ", + "fix": "Remove any demonstration and sample databases, database\n applications, objects, and files from the DBMS.\n\n To remove an account and all objects owned by that account (using BI as an\n example):\n DROP USER BI CASCADE;\n\n To remove objects without removing their owner, use the appropriate DROP\n statement (DROP TABLE, DROP VIEW, etc.)." + }, + "code": "", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61677.rb" + }, + "waiver_data": {}, + "results": [] + } + ], + "status": "loaded" + }, + { + "name": "Oracle Database 12c Security Technical Implementation Guide", + "version": "0.1.0", + "sha256": "1c5163a13ada389df3dc6c58f91b4e9b79df44e2c37fadfd67904c94012aee22", + "title": "Oracle Database 12c Security Technical Implementation Guide", + "maintainer": "The Authors", + "summary": "This Security Technical Implementation Guide is published as a tool to improve the security of Department of Defense (DoD) information systems. The requirements are derived from the National Institute of Standards and Technology (NIST) 800-53 and related documents. Comments or proposed revisions to this document should be sent via e-mail to the following address: disa.stig_spt@mail.mil.", + "license": "Apache-2.0", + "copyright": "The Authors", + "copyright_email": "you@example.com", + "supports": [], + "attributes": [], + "parent_profile": "aws-rds-oracle-database-12c-stig-baseline", + "groups": [ + { + "id": "controls/V-61409.rb", + "controls": [ + "V-61409" + ] + }, + { + "id": "controls/V-61449.rb", + "controls": [ + "V-61449" + ] + }, + { + "id": "controls/V-61867.rb", + "controls": [ + "V-61867" + ] + }, + { + "id": "controls/V-61635.rb", + "controls": [ + "V-61635" + ] + }, + { + "id": "controls/V-61677.rb", + "controls": [ + "V-61677" + ] + } + ], + "controls": [ + { + "id": "V-61409", + "title": "Audit trail data must be retained online for ninety (90) days and archived \n for old records for one (1) year.", + "desc": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding.", + "descriptions": [ + { + "label": "default", + "data": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding." + }, + { + "label": "check", + "data": "Develop, document and implement an audit retention policy and \n procedures.\n\n It is recommended that the most recent ninety days of audit logs remain \n available online.\n\n After thirty ninety days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to \n investigate suspicious activity." + } + ], + "impact": 0.5, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61409", + "rid": "SV-75899r1_rule", + "stig_id": "O121-BP-021100", + "fix_id": "F-67325r1_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review and verify the implementation of an audit trail\n retention policy.\n\n Verify that audit data is maintained for a minimum of one year.\n\n If audit data is not maintained for a minimum of one year, this is a finding.", + "fix": "Develop, document and implement an audit retention policy and\n procedures.\n\n It is recommended that the most recent thirty days of audit logs remain\n available online.\n\n After thirty days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to\n investigate suspicious activity." + }, + "code": "control 'V-61409' do\n title 'Audit trail data must be retained for at least one year.'\n desc \"Without preservation, a complete discovery of an attack or suspicious\n activity may not be determined. DBMS audit data also contributes to the\n complete investigation of unauthorized activity and needs to be included in\n audit retention plans and procedures.\"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000516-DB-999900'\n tag \"gid\": 'V-61409'\n tag \"rid\": 'SV-75899r1_rule'\n tag \"stig_id\": 'O121-BP-021100'\n tag \"fix_id\": 'F-67325r1_fix'\n tag \"cci\": ['CCI-000366']\n tag \"nist\": ['CM-6 b', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"Review and verify the implementation of an audit trail\n retention policy.\n\n Verify that audit data is maintained for a minimum of one year.\n\n If audit data is not maintained for a minimum of one year, this is a finding.\"\n tag \"fix\": \"Develop, document and implement an audit retention policy and\n procedures.\n\n It is recommended that the most recent thirty days of audit logs remain\n available online.\n\n After thirty days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to\n investigate suspicious activity.\"\n describe 'A manual review is required to ensure audit trail data is retained for at least one year' do\n skip 'A manual review is required to ensure audit trail data is retained for at least one year'\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61409.rb" + }, + "waiver_data": {}, + "results": [ + { + "status": "skipped", + "code_desc": "A manual review is required to ensure audit trail data is retained for at least one year", + "run_time": 8.916e-06, + "start_time": "2020-06-01T18:50:31+00:00", + "resource": "", + "skip_message": "A manual review is required to ensure audit trail data is retained for at least one year" + } + ] + }, + { + "id": "V-61449", + "title": "Database job/batch queues must be reviewed regularly to detect\n unauthorized database job submissions.", + "desc": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions.", + "descriptions": [ + { + "label": "default", + "data": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61449", + "rid": "SV-75939r3_rule", + "stig_id": "O121-BP-023100", + "fix_id": "F-67365r2_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "The DBMS_JOB PL/SQL package has been replaced by DBMS_SCHEDULER\n in Oracle versions 10.1 and higher, though it continues to be supported for\n backward compatibility.\n\n Run this query:\n select value from v$parameter where name = 'job_queue_processes';\n\n Run this query:\n select value from all_scheduler_global_attribute\n where ATTRIBUTE_NAME = 'MAX_JOB_SLAVE_PROCESSES';\n\n To understand the relationship between these settings, review:\n https://docs.oracle.com/database/121/ADMIN/appendix_a.htm#ADMIN11002\n\n Review documented and implemented procedures for monitoring the Oracle DBMS\n job/batch queues for unauthorized submissions. If procedures for job queue\n review are not defined, documented or evidence of implementation does not\n exist, this is a finding.\n\n Job queue information is available from the DBA_JOBS view. The following\n command lists jobs submitted to the queue. DBMS_JOB does not generate a\n 'history' of previous job executions.\n\n Run this query:\n select job, next_date, next_sec, failures, broken from dba_jobs;\n\n Scheduler queue information is available from the DBA_SCHEDULER_JOBS view. The\n following command lists jobs submitted to the queue.\n\n Run this query:\n select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;", + "fix": "Develop, document and implement procedures to monitor the\n database job queues for unauthorized job submissions.\n\n Develop, document and implement a formal migration plan to convert jobs using\n DBMS_JOB to use DBMS_SCHEDULER instead for Oracle versions 10.1 and higher.\n (This does not apply to DBMS_JOB jobs generated by Oracle itself, such as those\n for refreshing materialized views.)\n\n Set the value of the job_queue_processes parameter to a low value to restrict\n concurrent DBMS_JOB executions.\n\n Use auditing to capture use of the DBMS_JOB package in the audit trail. Review\n the audit trail for unauthorized use of the DBMS_JOB package." + }, + "code": "control 'V-61449' do\n title \"Database job/batch queues must be reviewed regularly to detect\n unauthorized database job submissions.\"\n desc \"Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions.\"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000516-DB-999900'\n tag \"gid\": 'V-61449'\n tag \"rid\": 'SV-75939r3_rule'\n tag \"stig_id\": 'O121-BP-023100'\n tag \"fix_id\": 'F-67365r2_fix'\n tag \"cci\": ['CCI-000366']\n tag \"nist\": ['CM-6 b', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"The DBMS_JOB PL/SQL package has been replaced by DBMS_SCHEDULER\n in Oracle versions 10.1 and higher, though it continues to be supported for\n backward compatibility.\n\n Run this query:\n select value from v$parameter where name = 'job_queue_processes';\n\n Run this query:\n select value from all_scheduler_global_attribute\n where ATTRIBUTE_NAME = 'MAX_JOB_SLAVE_PROCESSES';\n\n To understand the relationship between these settings, review:\n https://docs.oracle.com/database/121/ADMIN/appendix_a.htm#ADMIN11002\n\n Review documented and implemented procedures for monitoring the Oracle DBMS\n job/batch queues for unauthorized submissions. If procedures for job queue\n review are not defined, documented or evidence of implementation does not\n exist, this is a finding.\n\n Job queue information is available from the DBA_JOBS view. The following\n command lists jobs submitted to the queue. DBMS_JOB does not generate a\n 'history' of previous job executions.\n\n Run this query:\n select job, next_date, next_sec, failures, broken from dba_jobs;\n\n Scheduler queue information is available from the DBA_SCHEDULER_JOBS view. The\n following command lists jobs submitted to the queue.\n\n Run this query:\n select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;\"\n tag \"fix\": \"Develop, document and implement procedures to monitor the\n database job queues for unauthorized job submissions.\n\n Develop, document and implement a formal migration plan to convert jobs using\n DBMS_JOB to use DBMS_SCHEDULER instead for Oracle versions 10.1 and higher.\n (This does not apply to DBMS_JOB jobs generated by Oracle itself, such as those\n for refreshing materialized views.)\n\n Set the value of the job_queue_processes parameter to a low value to restrict\n concurrent DBMS_JOB executions.\n\n Use auditing to capture use of the DBMS_JOB package in the audit trail. Review\n the audit trail for unauthorized use of the DBMS_JOB package.\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n database_jobs = sql.query(\"select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;\").column('job_name')\n\n describe \"You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: #{database_jobs}\" do\n skip \"You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: #{database_jobs}\"\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61449.rb" + }, + "waiver_data": {}, + "results": [ + { + "status": "skipped", + "code_desc": "You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: [\"XMLDB_NFS_CLEANUP_JOB\", \"LOAD_OPATCH_INVENTORY\", \"SM$CLEAN_AUTO_SPLIT_MERGE\", \"RSE$CLEAN_RECOVERABLE_SCRIPT\", \"FGR$AUTOPURGE_JOB\", \"BSLN_MAINTAIN_STATS_JOB\", \"DRA_REEVALUATE_OPEN_FAILURES\", \"HM_CREATE_OFFLINE_DICTIONARY\", \"ORA$AUTOTASK_CLEAN\", \"FILE_SIZE_UPD\", \"CLEANUP_ONLINE_PMO\", \"CLEANUP_TRANSIENT_PKG\", \"CLEANUP_TRANSIENT_TYPE\", \"CLEANUP_TAB_IOT_PMO\", \"CLEANUP_ONLINE_IND_BUILD\", \"CLEANUP_NON_EXIST_OBJ\", \"PMO_DEFERRED_GIDX_MAINT_JOB\", \"FILE_WATCHER\", \"PURGE_LOG\"]", + "run_time": 4.062e-06, + "start_time": "2020-06-01T18:50:31+00:00", + "resource": "", + "skip_message": "You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: [\"XMLDB_NFS_CLEANUP_JOB\", \"LOAD_OPATCH_INVENTORY\", \"SM$CLEAN_AUTO_SPLIT_MERGE\", \"RSE$CLEAN_RECOVERABLE_SCRIPT\", \"FGR$AUTOPURGE_JOB\", \"BSLN_MAINTAIN_STATS_JOB\", \"DRA_REEVALUATE_OPEN_FAILURES\", \"HM_CREATE_OFFLINE_DICTIONARY\", \"ORA$AUTOTASK_CLEAN\", \"FILE_SIZE_UPD\", \"CLEANUP_ONLINE_PMO\", \"CLEANUP_TRANSIENT_PKG\", \"CLEANUP_TRANSIENT_TYPE\", \"CLEANUP_TAB_IOT_PMO\", \"CLEANUP_ONLINE_IND_BUILD\", \"CLEANUP_NON_EXIST_OBJ\", \"PMO_DEFERRED_GIDX_MAINT_JOB\", \"FILE_WATCHER\", \"PURGE_LOG\"]" + } + ] + }, + { + "id": "V-61867", + "title": "Database software, applications, and configuration files must be\n monitored to discover unauthorized changes.", + "desc": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations.", + "descriptions": [ + { + "label": "default", + "data": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations." + } + ], + "impact": 0.0, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000133-DB-000179", + "gid": "V-61867", + "rid": "SV-76357r2_rule", + "stig_id": "O121-OS-010700", + "fix_id": "F-67783r2_fix", + "cci": [ + "CCI-001499" + ], + "nist": [ + "CM-5 (6)", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review monitoring procedures and implementation evidence to\n verify that monitoring of changes to database software libraries, related\n applications, and configuration files is done.\n\n Verify that the list of files and directories being monitored is complete. If\n monitoring does not occur or is not complete, this is a finding.", + "fix": "Implement procedures to monitor for unauthorized changes to DBMS\n software libraries, related software application libraries, and configuration\n files. If a third-party automated tool is not employed, an automated job that\n reports file information on the directories and files of interest and compares\n them to the baseline report for the same will meet the requirement.\n\n File hashes or checksums should be used for comparisons since file dates may be\n manipulated by malicious users." + }, + "code": "control 'V-61867' do\n title \"Database software, applications, and configuration files must be\n monitored to discover unauthorized changes.\"\n desc \"Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000133-DB-000179'\n tag \"gid\": 'V-61867'\n tag \"rid\": 'SV-76357r2_rule'\n tag \"stig_id\": 'O121-OS-010700'\n tag \"fix_id\": 'F-67783r2_fix'\n tag \"cci\": ['CCI-001499']\n tag \"nist\": ['CM-5 (6)', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"Review monitoring procedures and implementation evidence to\n verify that monitoring of changes to database software libraries, related\n applications, and configuration files is done.\n\n Verify that the list of files and directories being monitored is complete. If\n monitoring does not occur or is not complete, this is a finding.\"\n tag \"fix\": \"Implement procedures to monitor for unauthorized changes to DBMS\n software libraries, related software application libraries, and configuration\n files. If a third-party automated tool is not employed, an automated job that\n reports file information on the directories and files of interest and compares\n them to the baseline report for the same will meet the requirement.\n\n File hashes or checksums should be used for comparisons since file dates may be\n manipulated by malicious users.\"\n describe command('grep aide /etc/crontab /etc/cron.*/*') do\n its('stdout.strip') { should_not be_empty }\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61867.rb" + }, + "waiver_data": {}, + "results": [ + { + "status": "skipped", + "code_desc": "This control is not applicable on oracle within aws rds, as aws manages the operating system in which the oracle database is running on", + "run_time": 7.239e-06, + "start_time": "2020-06-01T18:50:31+00:00", + "resource": "", + "skip_message": "This control is not applicable on oracle within aws rds, as aws manages the operating system in which the oracle database is running on" + } + ] + }, + { + "id": "V-61635", + "title": "The DBMS must produce audit records containing sufficient information\n to establish the sources (origins) of the events.", + "desc": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable.", + "descriptions": [ + { + "label": "default", + "data": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000098-DB-000042", + "gid": "V-61635", + "rid": "SV-76125r1_rule", + "stig_id": "O121-C2-007700", + "fix_id": "F-67547r1_fix", + "cci": [ + "CCI-000133" + ], + "nist": [ + "AU-3", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Verify, using vendor and system documentation if necessary,\n that the DBMS is configured to use Oracle's auditing features, or that a\n third-party product or custom code is deployed and configured to satisfy this\n requirement.\n\n If a third-party product or custom code is used, compare its current\n configuration with the audit requirements. If any of the requirements is not\n covered by the configuration, this is a finding.\n\n The remainder of this Check is applicable specifically where Oracle auditing is\n in use.\n\n If Standard Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SHOW PARAMETER AUDIT_TRAIL\n\n or the following SQL query:\n\n SELECT * FROM SYS.V$PARAMETER WHERE NAME = 'audit_trail';\n\n If Oracle returns the value 'NONE', this is a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the SYS.AUD$\n table or the audit file, whichever is in use.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\n\n If Unified Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SELECT * FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\n\n If Oracle returns the value \"TRUE\", this is not a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the\n SYS.UNIFIED_AUDIT_TRAIL view.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.", + "fix": "Configure the DBMS's auditing to audit standard and\n organization-defined auditable events, the audit record to include the source\n of the event. If preferred, use a third-party or custom tool.\n\n If using a third-party product, proceed in accordance with the product\n documentation. If using Oracle's capabilities, proceed as follows.\n\n If Standard Auditing is used:\n Use this process to ensure auditable events are captured:\n\n ALTER SYSTEM SET AUDIT_TRAIL= SCOPE=SPFILE;\n\n Audit trail type can be 'OS', 'DB', 'DB,EXTENDED', 'XML' or 'XML,EXTENDED'.\n After executing this statement, it may be necessary to shut down and restart\n the Oracle database.\n\n If Unified Auditing is used:\n To ensure auditable events are captured:\n Link the oracle binary with uniaud_on, and then restart the database.\n\n\n\n Oracle Database Upgrade Guide describes how to enable unified auditing.\n\n For more information on the configuration of auditing, refer to the following\n documents:\n \"Auditing Database Activity\" in the Oracle Database 2 Day + Security Guide:\n http://docs.oracle.com/database/121/TDPSG/tdpsg_auditing.htm#TDPSG50000\n \"Monitoring Database Activity with Auditing\" in the Oracle Database Security\n Guide:\n http://docs.oracle.com/database/121/DBSEG/part_6.htm#CCHEHCGI\n \"DBMS_AUDIT_MGMT\" in the Oracle Database PL/SQL Packages and Types Reference:\n http://docs.oracle.com/database/121/ARPLS/d_audit_mgmt.htm#ARPLS241\n Oracle Database Upgrade Guide:\n http://docs.oracle.com/database/121/UPGRD/afterup.htm#UPGRD52810" + }, + "code": "control 'V-61635' do\n title \"The DBMS must produce audit records containing sufficient information\n to establish the sources (origins) of the events.\"\n desc \"Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000098-DB-000042'\n tag \"gid\": 'V-61635'\n tag \"rid\": 'SV-76125r1_rule'\n tag \"stig_id\": 'O121-C2-007700'\n tag \"fix_id\": 'F-67547r1_fix'\n tag \"cci\": ['CCI-000133']\n tag \"nist\": ['AU-3', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"Verify, using vendor and system documentation if necessary,\n that the DBMS is configured to use Oracle's auditing features, or that a\n third-party product or custom code is deployed and configured to satisfy this\n requirement.\n\n If a third-party product or custom code is used, compare its current\n configuration with the audit requirements. If any of the requirements is not\n covered by the configuration, this is a finding.\n\n The remainder of this Check is applicable specifically where Oracle auditing is\n in use.\n\n If Standard Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SHOW PARAMETER AUDIT_TRAIL\n\n or the following SQL query:\n\n SELECT * FROM SYS.V$PARAMETER WHERE NAME = 'audit_trail';\n\n If Oracle returns the value 'NONE', this is a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the SYS.AUD$\n table or the audit file, whichever is in use.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\n\n If Unified Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SELECT * FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\n\n If Oracle returns the value \\\"TRUE\\\", this is not a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the\n SYS.UNIFIED_AUDIT_TRAIL view.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\"\n tag \"fix\": \"Configure the DBMS's auditing to audit standard and\n organization-defined auditable events, the audit record to include the source\n of the event. If preferred, use a third-party or custom tool.\n\n If using a third-party product, proceed in accordance with the product\n documentation. If using Oracle's capabilities, proceed as follows.\n\n If Standard Auditing is used:\n Use this process to ensure auditable events are captured:\n\n ALTER SYSTEM SET AUDIT_TRAIL= SCOPE=SPFILE;\n\n Audit trail type can be 'OS', 'DB', 'DB,EXTENDED', 'XML' or 'XML,EXTENDED'.\n After executing this statement, it may be necessary to shut down and restart\n the Oracle database.\n\n If Unified Auditing is used:\n To ensure auditable events are captured:\n Link the oracle binary with uniaud_on, and then restart the database.\n\n\n\n Oracle Database Upgrade Guide describes how to enable unified auditing.\n\n For more information on the configuration of auditing, refer to the following\n documents:\n \\\"Auditing Database Activity\\\" in the Oracle Database 2 Day + Security Guide:\n http://docs.oracle.com/database/121/TDPSG/tdpsg_auditing.htm#TDPSG50000\n \\\"Monitoring Database Activity with Auditing\\\" in the Oracle Database Security\n Guide:\n http://docs.oracle.com/database/121/DBSEG/part_6.htm#CCHEHCGI\n \\\"DBMS_AUDIT_MGMT\\\" in the Oracle Database PL/SQL Packages and Types Reference:\n http://docs.oracle.com/database/121/ARPLS/d_audit_mgmt.htm#ARPLS241\n Oracle Database Upgrade Guide:\n http://docs.oracle.com/database/121/UPGRD/afterup.htm#UPGRD52810\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n standard_auditing_used = input('standard_auditing_used')\n unified_auditing_used = input('unified_auditing_used')\n\n describe.one do\n describe 'Standard auditing is in use for audit purposes' do\n subject { standard_auditing_used }\n it { should be true }\n end\n\n describe 'Unified auditing is in use for audit purposes' do\n subject { unified_auditing_used }\n it { should be true }\n end\n end\n\n audit_trail = sql.query(\"select value from v$parameter where name = 'audit_trail';\").column('value')\n audit_info_captured = sql.query('SELECT * FROM UNIFIED_AUDIT_TRAIL;').column('EVENT_TIMESTAMP')\n\n if standard_auditing_used\n describe 'The oracle database audit_trail parameter' do\n subject { audit_trail }\n it { should_not cmp 'NONE' }\n end\n end\n\n unified_auditing = sql.query(\"SELECT value FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\").column('value')\n\n if unified_auditing_used\n describe 'The oracle database unified auditing parameter' do\n subject { unified_auditing }\n it { should_not cmp 'FALSE' }\n end\n\n describe 'The oracle database unified auditing events captured' do\n subject { audit_info_captured }\n it { should_not be_empty }\n end\n\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61635.rb" + }, + "waiver_data": {}, + "results": [ + { + "status": "passed", + "code_desc": "Standard auditing is in use for audit purposes is expected to equal true", + "run_time": 6.8198e-05, + "start_time": "2020-06-01T18:50:31+00:00" + }, + { + "status": "failed", + "code_desc": "The oracle database audit_trail parameter is expected not to cmp == \"NONE\"", + "run_time": 0.000259495, + "start_time": "2020-06-01T18:50:31+00:00", + "message": "\nexpected: NONE\n got: [\"NONE\"]\n\n(compared using `cmp` matcher)\n" + } + ] + }, + { + "id": "V-61677", + "title": "Default demonstration and sample databases, database objects, and\n applications must be removed.", + "desc": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system.", + "descriptions": [ + { + "label": "default", + "data": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000141-DB-000090", + "gid": "V-61677", + "rid": "SV-76167r3_rule", + "stig_id": "O121-C2-011500", + "fix_id": "F-67591r1_fix", + "cci": [ + "CCI-000381" + ], + "nist": [ + "CM-7 a", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "If Oracle is hosted on a server that does not support\n production systems, and is designated for the deployment of samples and\n demonstrations, this is not applicable (NA).\n\n Review documentation and websites from Oracle and any other relevant vendors\n for vendor-provided demonstration or sample databases, database applications,\n schemas, objects, and files.\n\n Review the Oracle DBMS to determine if any of the demonstration and sample\n databases, schemas, database applications, or files are installed in the\n database or are included with the DBMS application. If any are present in the\n database or are included with the DBMS application, this is a finding.\n\n The Oracle Default Sample Schema User Accounts are:\n\n BI\n Owns the Business Intelligence schema included in the Oracle Sample Schemas.\n\n HR\n Manages the Human Resources schema. Schema stores information about the\n employees and the facilities of the company.\n\n OE\n Manages the Order Entry schema. Schema stores product inventories and sales of\n the company's products through various channels.\n\n PM\n Manages the Product Media schema. Schema contains descriptions and detailed\n information about each product sold by the company.\n\n IX\n Manages the Information Exchange schema. Schema manages shipping through\n business-to-business (B2B) applications database.\n\n SH\n Manages the Sales schema. Schema stores statistics to facilitate business\n decisions.\n\n SCOTT\n A demonstration account with a simple schema.\n\n Connect to Oracle as SYSDBA; run the following SQL to check for presence of\n Oracle Default Sample Schema User Accounts:\n select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\n\n If any of the users listed above is returned it means that there are demo\n programs installed, so this is a finding.\n ", + "fix": "Remove any demonstration and sample databases, database\n applications, objects, and files from the DBMS.\n\n To remove an account and all objects owned by that account (using BI as an\n example):\n DROP USER BI CASCADE;\n\n To remove objects without removing their owner, use the appropriate DROP\n statement (DROP TABLE, DROP VIEW, etc.)." + }, + "code": "control 'V-61677' do\n title \"Default demonstration and sample databases, database objects, and\n applications must be removed.\"\n desc \"Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000141-DB-000090'\n tag \"gid\": 'V-61677'\n tag \"rid\": 'SV-76167r3_rule'\n tag \"stig_id\": 'O121-C2-011500'\n tag \"fix_id\": 'F-67591r1_fix'\n tag \"cci\": ['CCI-000381']\n tag \"nist\": ['CM-7 a', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"If Oracle is hosted on a server that does not support\n production systems, and is designated for the deployment of samples and\n demonstrations, this is not applicable (NA).\n\n Review documentation and websites from Oracle and any other relevant vendors\n for vendor-provided demonstration or sample databases, database applications,\n schemas, objects, and files.\n\n Review the Oracle DBMS to determine if any of the demonstration and sample\n databases, schemas, database applications, or files are installed in the\n database or are included with the DBMS application. If any are present in the\n database or are included with the DBMS application, this is a finding.\n\n The Oracle Default Sample Schema User Accounts are:\n\n BI\n Owns the Business Intelligence schema included in the Oracle Sample Schemas.\n\n HR\n Manages the Human Resources schema. Schema stores information about the\n employees and the facilities of the company.\n\n OE\n Manages the Order Entry schema. Schema stores product inventories and sales of\n the company's products through various channels.\n\n PM\n Manages the Product Media schema. Schema contains descriptions and detailed\n information about each product sold by the company.\n\n IX\n Manages the Information Exchange schema. Schema manages shipping through\n business-to-business (B2B) applications database.\n\n SH\n Manages the Sales schema. Schema stores statistics to facilitate business\n decisions.\n\n SCOTT\n A demonstration account with a simple schema.\n\n Connect to Oracle as SYSDBA; run the following SQL to check for presence of\n Oracle Default Sample Schema User Accounts:\n select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\n\n If any of the users listed above is returned it means that there are demo\n programs installed, so this is a finding.\n \"\n tag \"fix\": \"Remove any demonstration and sample databases, database\n applications, objects, and files from the DBMS.\n\n To remove an account and all objects owned by that account (using BI as an\n example):\n DROP USER BI CASCADE;\n\n To remove objects without removing their owner, use the appropriate DROP\n statement (DROP TABLE, DROP VIEW, etc.).\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n sample_schema_user_accounts = sql.query(\"select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\").column('username')\n\n describe 'The list of oracle default sample schema user accounts' do\n subject { sample_schema_user_accounts }\n it { should be_empty }\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61677.rb" + }, + "waiver_data": {}, + "results": [ + { + "status": "passed", + "code_desc": "The list of oracle default sample schema user accounts is expected to be empty", + "run_time": 0.01266834, + "start_time": "2020-06-01T18:50:31+00:00" + } + ] + } + ], + "status": "loaded" + } + ], + "statistics": { + "duration": 0.353633098 + }, + "version": "4.18.100" +} \ No newline at end of file diff --git a/test/sample_data/attestations/triple_overlay_attested.json b/test/sample_data/attestations/triple_overlay_attested.json new file mode 100644 index 000000000..948e1a1f3 --- /dev/null +++ b/test/sample_data/attestations/triple_overlay_attested.json @@ -0,0 +1,1275 @@ +{ + "platform": { + "name": "centos", + "release": "7.7.1908" + }, + "profiles": [ + { + "name": "cms-ars-3.1-moderate-aws-rds-oracle-database-12c-stig-overlay", + "version": "0.1.0", + "sha256": "3fe40f9476a23b5b4dd6c0da2bb8dbe8ca5a4a8b6bfb27ffbf9f1797160c0f91", + "title": ".", + "maintainer": "CMS InSpec Dev Team", + "summary": ".", + "license": "Apache-2.0", + "copyright": ".", + "supports": [], + "attributes": [ + { + "name": "user", + "options": { + "value": "admin1" + } + }, + { + "name": "password", + "options": { + "value": "" + } + }, + { + "name": "host", + "options": { + "value": "oracle.host" + } + }, + { + "name": "service", + "options": { + "value": "ORCL" + } + }, + { + "name": "sqlplus_bin", + "options": { + "value": "/usr/bin/sqlplus" + } + }, + { + "name": "standard_auditing_used", + "options": { + "type": "Boolean", + "value": false + } + }, + { + "name": "unified_auditing_used", + "options": { + "type": "Boolean", + "value": false + } + }, + { + "name": "allowed_db_links", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_dbadmin_users", + "options": { + "value": [] + } + }, + { + "name": "users_allowed_access_to_public", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_dba_role", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_system_tablespace", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_application_owners", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_unlocked_oracledb_accounts", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "users_allowed_access_to_dictionary_table", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_with_admin_privs", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_audit_users", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_dbaobject_owners", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_oracledb_components", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_oracledb_components_integrated_into_dbms", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "oracle_dbas", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "emergency_profile_list", + "options": { + "type": "Array", + "value": [ + "RDSADMIN" + ] + } + } + ], + "depends": [ + { + "name": "aws-rds-oracle-database-12c-stig-baseline", + "path": "../aws-rds-oracle-database-12c-stig-baseline", + "status": "loaded" + } + ], + "groups": [ + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61409.rb", + "controls": [ + "V-61409" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61449.rb", + "controls": [ + "V-61449" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61867.rb", + "controls": [ + "V-61867" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61635.rb", + "controls": [ + "V-61635" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61677.rb", + "controls": [ + "V-61677" + ] + } + ], + "controls": [ + { + "id": "V-61409", + "title": "Audit trail data must be retained online for ninety (90) days and archived \n for old records for one (1) year.", + "desc": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding.", + "descriptions": [ + { + "label": "default", + "data": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding." + }, + { + "label": "check", + "data": "Develop, document and implement an audit retention policy and \n procedures.\n\n It is recommended that the most recent ninety days of audit logs remain \n available online.\n\n After thirty ninety days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to \n investigate suspicious activity." + } + ], + "impact": 0.5, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61409", + "rid": "SV-75899r1_rule", + "stig_id": "O121-BP-021100", + "fix_id": "F-67325r1_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review and verify the implementation of an audit trail\n retention policy.\n\n Verify that audit data is maintained for a minimum of one year.\n\n If audit data is not maintained for a minimum of one year, this is a finding.", + "fix": "Develop, document and implement an audit retention policy and\n procedures.\n\n It is recommended that the most recent thirty days of audit logs remain\n available online.\n\n After thirty days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to\n investigate suspicious activity." + }, + "code": " control 'V-61409' do\n title 'Audit trail data must be retained online for ninety (90) days and archived \n for old records for one (1) year.'\n desc 'Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding.'\n desc 'check', 'Develop, document and implement an audit retention policy and \n procedures.\n\n It is recommended that the most recent ninety days of audit logs remain \n available online.\n\n After thirty ninety days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to \n investigate suspicious activity.'\n end\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61409.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61449", + "title": "Database job/batch queues must be reviewed regularly to detect\n unauthorized database job submissions.", + "desc": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions.", + "descriptions": [ + { + "label": "default", + "data": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61449", + "rid": "SV-75939r3_rule", + "stig_id": "O121-BP-023100", + "fix_id": "F-67365r2_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "The DBMS_JOB PL/SQL package has been replaced by DBMS_SCHEDULER\n in Oracle versions 10.1 and higher, though it continues to be supported for\n backward compatibility.\n\n Run this query:\n select value from v$parameter where name = 'job_queue_processes';\n\n Run this query:\n select value from all_scheduler_global_attribute\n where ATTRIBUTE_NAME = 'MAX_JOB_SLAVE_PROCESSES';\n\n To understand the relationship between these settings, review:\n https://docs.oracle.com/database/121/ADMIN/appendix_a.htm#ADMIN11002\n\n Review documented and implemented procedures for monitoring the Oracle DBMS\n job/batch queues for unauthorized submissions. If procedures for job queue\n review are not defined, documented or evidence of implementation does not\n exist, this is a finding.\n\n Job queue information is available from the DBA_JOBS view. The following\n command lists jobs submitted to the queue. DBMS_JOB does not generate a\n 'history' of previous job executions.\n\n Run this query:\n select job, next_date, next_sec, failures, broken from dba_jobs;\n\n Scheduler queue information is available from the DBA_SCHEDULER_JOBS view. The\n following command lists jobs submitted to the queue.\n\n Run this query:\n select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;", + "fix": "Develop, document and implement procedures to monitor the\n database job queues for unauthorized job submissions.\n\n Develop, document and implement a formal migration plan to convert jobs using\n DBMS_JOB to use DBMS_SCHEDULER instead for Oracle versions 10.1 and higher.\n (This does not apply to DBMS_JOB jobs generated by Oracle itself, such as those\n for refreshing materialized views.)\n\n Set the value of the job_queue_processes parameter to a low value to restrict\n concurrent DBMS_JOB executions.\n\n Use auditing to capture use of the DBMS_JOB package in the audit trail. Review\n the audit trail for unauthorized use of the DBMS_JOB package." + }, + "code": "control 'V-61449' do\n title \"Database job/batch queues must be reviewed regularly to detect\n unauthorized database job submissions.\"\n desc \"Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions.\"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000516-DB-999900'\n tag \"gid\": 'V-61449'\n tag \"rid\": 'SV-75939r3_rule'\n tag \"stig_id\": 'O121-BP-023100'\n tag \"fix_id\": 'F-67365r2_fix'\n tag \"cci\": ['CCI-000366']\n tag \"nist\": ['CM-6 b', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"The DBMS_JOB PL/SQL package has been replaced by DBMS_SCHEDULER\n in Oracle versions 10.1 and higher, though it continues to be supported for\n backward compatibility.\n\n Run this query:\n select value from v$parameter where name = 'job_queue_processes';\n\n Run this query:\n select value from all_scheduler_global_attribute\n where ATTRIBUTE_NAME = 'MAX_JOB_SLAVE_PROCESSES';\n\n To understand the relationship between these settings, review:\n https://docs.oracle.com/database/121/ADMIN/appendix_a.htm#ADMIN11002\n\n Review documented and implemented procedures for monitoring the Oracle DBMS\n job/batch queues for unauthorized submissions. If procedures for job queue\n review are not defined, documented or evidence of implementation does not\n exist, this is a finding.\n\n Job queue information is available from the DBA_JOBS view. The following\n command lists jobs submitted to the queue. DBMS_JOB does not generate a\n 'history' of previous job executions.\n\n Run this query:\n select job, next_date, next_sec, failures, broken from dba_jobs;\n\n Scheduler queue information is available from the DBA_SCHEDULER_JOBS view. The\n following command lists jobs submitted to the queue.\n\n Run this query:\n select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;\"\n tag \"fix\": \"Develop, document and implement procedures to monitor the\n database job queues for unauthorized job submissions.\n\n Develop, document and implement a formal migration plan to convert jobs using\n DBMS_JOB to use DBMS_SCHEDULER instead for Oracle versions 10.1 and higher.\n (This does not apply to DBMS_JOB jobs generated by Oracle itself, such as those\n for refreshing materialized views.)\n\n Set the value of the job_queue_processes parameter to a low value to restrict\n concurrent DBMS_JOB executions.\n\n Use auditing to capture use of the DBMS_JOB package in the audit trail. Review\n the audit trail for unauthorized use of the DBMS_JOB package.\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n database_jobs = sql.query(\"select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;\").column('job_name')\n\n describe \"You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: #{database_jobs}\" do\n skip \"You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: #{database_jobs}\"\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61449.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61635", + "title": "The DBMS must produce audit records containing sufficient information\n to establish the sources (origins) of the events.", + "desc": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable.", + "descriptions": [ + { + "label": "default", + "data": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000098-DB-000042", + "gid": "V-61635", + "rid": "SV-76125r1_rule", + "stig_id": "O121-C2-007700", + "fix_id": "F-67547r1_fix", + "cci": [ + "CCI-000133" + ], + "nist": [ + "AU-3", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Verify, using vendor and system documentation if necessary,\n that the DBMS is configured to use Oracle's auditing features, or that a\n third-party product or custom code is deployed and configured to satisfy this\n requirement.\n\n If a third-party product or custom code is used, compare its current\n configuration with the audit requirements. If any of the requirements is not\n covered by the configuration, this is a finding.\n\n The remainder of this Check is applicable specifically where Oracle auditing is\n in use.\n\n If Standard Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SHOW PARAMETER AUDIT_TRAIL\n\n or the following SQL query:\n\n SELECT * FROM SYS.V$PARAMETER WHERE NAME = 'audit_trail';\n\n If Oracle returns the value 'NONE', this is a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the SYS.AUD$\n table or the audit file, whichever is in use.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\n\n If Unified Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SELECT * FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\n\n If Oracle returns the value \"TRUE\", this is not a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the\n SYS.UNIFIED_AUDIT_TRAIL view.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.", + "fix": "Configure the DBMS's auditing to audit standard and\n organization-defined auditable events, the audit record to include the source\n of the event. If preferred, use a third-party or custom tool.\n\n If using a third-party product, proceed in accordance with the product\n documentation. If using Oracle's capabilities, proceed as follows.\n\n If Standard Auditing is used:\n Use this process to ensure auditable events are captured:\n\n ALTER SYSTEM SET AUDIT_TRAIL= SCOPE=SPFILE;\n\n Audit trail type can be 'OS', 'DB', 'DB,EXTENDED', 'XML' or 'XML,EXTENDED'.\n After executing this statement, it may be necessary to shut down and restart\n the Oracle database.\n\n If Unified Auditing is used:\n To ensure auditable events are captured:\n Link the oracle binary with uniaud_on, and then restart the database.\n\n\n\n Oracle Database Upgrade Guide describes how to enable unified auditing.\n\n For more information on the configuration of auditing, refer to the following\n documents:\n \"Auditing Database Activity\" in the Oracle Database 2 Day + Security Guide:\n http://docs.oracle.com/database/121/TDPSG/tdpsg_auditing.htm#TDPSG50000\n \"Monitoring Database Activity with Auditing\" in the Oracle Database Security\n Guide:\n http://docs.oracle.com/database/121/DBSEG/part_6.htm#CCHEHCGI\n \"DBMS_AUDIT_MGMT\" in the Oracle Database PL/SQL Packages and Types Reference:\n http://docs.oracle.com/database/121/ARPLS/d_audit_mgmt.htm#ARPLS241\n Oracle Database Upgrade Guide:\n http://docs.oracle.com/database/121/UPGRD/afterup.htm#UPGRD52810" + }, + "code": "control 'V-61635' do\n title \"The DBMS must produce audit records containing sufficient information\n to establish the sources (origins) of the events.\"\n desc \"Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000098-DB-000042'\n tag \"gid\": 'V-61635'\n tag \"rid\": 'SV-76125r1_rule'\n tag \"stig_id\": 'O121-C2-007700'\n tag \"fix_id\": 'F-67547r1_fix'\n tag \"cci\": ['CCI-000133']\n tag \"nist\": ['AU-3', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"Verify, using vendor and system documentation if necessary,\n that the DBMS is configured to use Oracle's auditing features, or that a\n third-party product or custom code is deployed and configured to satisfy this\n requirement.\n\n If a third-party product or custom code is used, compare its current\n configuration with the audit requirements. If any of the requirements is not\n covered by the configuration, this is a finding.\n\n The remainder of this Check is applicable specifically where Oracle auditing is\n in use.\n\n If Standard Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SHOW PARAMETER AUDIT_TRAIL\n\n or the following SQL query:\n\n SELECT * FROM SYS.V$PARAMETER WHERE NAME = 'audit_trail';\n\n If Oracle returns the value 'NONE', this is a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the SYS.AUD$\n table or the audit file, whichever is in use.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\n\n If Unified Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SELECT * FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\n\n If Oracle returns the value \\\"TRUE\\\", this is not a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the\n SYS.UNIFIED_AUDIT_TRAIL view.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\"\n tag \"fix\": \"Configure the DBMS's auditing to audit standard and\n organization-defined auditable events, the audit record to include the source\n of the event. If preferred, use a third-party or custom tool.\n\n If using a third-party product, proceed in accordance with the product\n documentation. If using Oracle's capabilities, proceed as follows.\n\n If Standard Auditing is used:\n Use this process to ensure auditable events are captured:\n\n ALTER SYSTEM SET AUDIT_TRAIL= SCOPE=SPFILE;\n\n Audit trail type can be 'OS', 'DB', 'DB,EXTENDED', 'XML' or 'XML,EXTENDED'.\n After executing this statement, it may be necessary to shut down and restart\n the Oracle database.\n\n If Unified Auditing is used:\n To ensure auditable events are captured:\n Link the oracle binary with uniaud_on, and then restart the database.\n\n\n\n Oracle Database Upgrade Guide describes how to enable unified auditing.\n\n For more information on the configuration of auditing, refer to the following\n documents:\n \\\"Auditing Database Activity\\\" in the Oracle Database 2 Day + Security Guide:\n http://docs.oracle.com/database/121/TDPSG/tdpsg_auditing.htm#TDPSG50000\n \\\"Monitoring Database Activity with Auditing\\\" in the Oracle Database Security\n Guide:\n http://docs.oracle.com/database/121/DBSEG/part_6.htm#CCHEHCGI\n \\\"DBMS_AUDIT_MGMT\\\" in the Oracle Database PL/SQL Packages and Types Reference:\n http://docs.oracle.com/database/121/ARPLS/d_audit_mgmt.htm#ARPLS241\n Oracle Database Upgrade Guide:\n http://docs.oracle.com/database/121/UPGRD/afterup.htm#UPGRD52810\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n standard_auditing_used = input('standard_auditing_used')\n unified_auditing_used = input('unified_auditing_used')\n\n describe.one do\n describe 'Standard auditing is in use for audit purposes' do\n subject { standard_auditing_used }\n it { should be true }\n end\n\n describe 'Unified auditing is in use for audit purposes' do\n subject { unified_auditing_used }\n it { should be true }\n end\n end\n\n audit_trail = sql.query(\"select value from v$parameter where name = 'audit_trail';\").column('value')\n audit_info_captured = sql.query('SELECT * FROM UNIFIED_AUDIT_TRAIL;').column('EVENT_TIMESTAMP')\n\n if standard_auditing_used\n describe 'The oracle database audit_trail parameter' do\n subject { audit_trail }\n it { should_not cmp 'NONE' }\n end\n end\n\n unified_auditing = sql.query(\"SELECT value FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\").column('value')\n\n if unified_auditing_used\n describe 'The oracle database unified auditing parameter' do\n subject { unified_auditing }\n it { should_not cmp 'FALSE' }\n end\n\n describe 'The oracle database unified auditing events captured' do\n subject { audit_info_captured }\n it { should_not be_empty }\n end\n\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61635.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61867", + "title": "Database software, applications, and configuration files must be\n monitored to discover unauthorized changes.", + "desc": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations.", + "descriptions": [ + { + "label": "default", + "data": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations." + } + ], + "impact": 0, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000133-DB-000179", + "gid": "V-61867", + "rid": "SV-76357r2_rule", + "stig_id": "O121-OS-010700", + "fix_id": "F-67783r2_fix", + "cci": [ + "CCI-001499" + ], + "nist": [ + "CM-5 (6)", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review monitoring procedures and implementation evidence to\n verify that monitoring of changes to database software libraries, related\n applications, and configuration files is done.\n\n Verify that the list of files and directories being monitored is complete. If\n monitoring does not occur or is not complete, this is a finding.", + "fix": "Implement procedures to monitor for unauthorized changes to DBMS\n software libraries, related software application libraries, and configuration\n files. If a third-party automated tool is not employed, an automated job that\n reports file information on the directories and files of interest and compares\n them to the baseline report for the same will meet the requirement.\n\n File hashes or checksums should be used for comparisons since file dates may be\n manipulated by malicious users." + }, + "code": "control 'V-61867' do\n title \"Database software, applications, and configuration files must be\n monitored to discover unauthorized changes.\"\n desc \"Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000133-DB-000179'\n tag \"gid\": 'V-61867'\n tag \"rid\": 'SV-76357r2_rule'\n tag \"stig_id\": 'O121-OS-010700'\n tag \"fix_id\": 'F-67783r2_fix'\n tag \"cci\": ['CCI-001499']\n tag \"nist\": ['CM-5 (6)', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"Review monitoring procedures and implementation evidence to\n verify that monitoring of changes to database software libraries, related\n applications, and configuration files is done.\n\n Verify that the list of files and directories being monitored is complete. If\n monitoring does not occur or is not complete, this is a finding.\"\n tag \"fix\": \"Implement procedures to monitor for unauthorized changes to DBMS\n software libraries, related software application libraries, and configuration\n files. If a third-party automated tool is not employed, an automated job that\n reports file information on the directories and files of interest and compares\n them to the baseline report for the same will meet the requirement.\n\n File hashes or checksums should be used for comparisons since file dates may be\n manipulated by malicious users.\"\n describe command('grep aide /etc/crontab /etc/cron.*/*') do\n its('stdout.strip') { should_not be_empty }\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61867.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61677", + "title": "Default demonstration and sample databases, database objects, and\n applications must be removed.", + "desc": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system.", + "descriptions": [ + { + "label": "default", + "data": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000141-DB-000090", + "gid": "V-61677", + "rid": "SV-76167r3_rule", + "stig_id": "O121-C2-011500", + "fix_id": "F-67591r1_fix", + "cci": [ + "CCI-000381" + ], + "nist": [ + "CM-7 a", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "If Oracle is hosted on a server that does not support\n production systems, and is designated for the deployment of samples and\n demonstrations, this is not applicable (NA).\n\n Review documentation and websites from Oracle and any other relevant vendors\n for vendor-provided demonstration or sample databases, database applications,\n schemas, objects, and files.\n\n Review the Oracle DBMS to determine if any of the demonstration and sample\n databases, schemas, database applications, or files are installed in the\n database or are included with the DBMS application. If any are present in the\n database or are included with the DBMS application, this is a finding.\n\n The Oracle Default Sample Schema User Accounts are:\n\n BI\n Owns the Business Intelligence schema included in the Oracle Sample Schemas.\n\n HR\n Manages the Human Resources schema. Schema stores information about the\n employees and the facilities of the company.\n\n OE\n Manages the Order Entry schema. Schema stores product inventories and sales of\n the company's products through various channels.\n\n PM\n Manages the Product Media schema. Schema contains descriptions and detailed\n information about each product sold by the company.\n\n IX\n Manages the Information Exchange schema. Schema manages shipping through\n business-to-business (B2B) applications database.\n\n SH\n Manages the Sales schema. Schema stores statistics to facilitate business\n decisions.\n\n SCOTT\n A demonstration account with a simple schema.\n\n Connect to Oracle as SYSDBA; run the following SQL to check for presence of\n Oracle Default Sample Schema User Accounts:\n select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\n\n If any of the users listed above is returned it means that there are demo\n programs installed, so this is a finding.\n ", + "fix": "Remove any demonstration and sample databases, database\n applications, objects, and files from the DBMS.\n\n To remove an account and all objects owned by that account (using BI as an\n example):\n DROP USER BI CASCADE;\n\n To remove objects without removing their owner, use the appropriate DROP\n statement (DROP TABLE, DROP VIEW, etc.)." + }, + "code": "control 'V-61677' do\n title \"Default demonstration and sample databases, database objects, and\n applications must be removed.\"\n desc \"Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000141-DB-000090'\n tag \"gid\": 'V-61677'\n tag \"rid\": 'SV-76167r3_rule'\n tag \"stig_id\": 'O121-C2-011500'\n tag \"fix_id\": 'F-67591r1_fix'\n tag \"cci\": ['CCI-000381']\n tag \"nist\": ['CM-7 a', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"If Oracle is hosted on a server that does not support\n production systems, and is designated for the deployment of samples and\n demonstrations, this is not applicable (NA).\n\n Review documentation and websites from Oracle and any other relevant vendors\n for vendor-provided demonstration or sample databases, database applications,\n schemas, objects, and files.\n\n Review the Oracle DBMS to determine if any of the demonstration and sample\n databases, schemas, database applications, or files are installed in the\n database or are included with the DBMS application. If any are present in the\n database or are included with the DBMS application, this is a finding.\n\n The Oracle Default Sample Schema User Accounts are:\n\n BI\n Owns the Business Intelligence schema included in the Oracle Sample Schemas.\n\n HR\n Manages the Human Resources schema. Schema stores information about the\n employees and the facilities of the company.\n\n OE\n Manages the Order Entry schema. Schema stores product inventories and sales of\n the company's products through various channels.\n\n PM\n Manages the Product Media schema. Schema contains descriptions and detailed\n information about each product sold by the company.\n\n IX\n Manages the Information Exchange schema. Schema manages shipping through\n business-to-business (B2B) applications database.\n\n SH\n Manages the Sales schema. Schema stores statistics to facilitate business\n decisions.\n\n SCOTT\n A demonstration account with a simple schema.\n\n Connect to Oracle as SYSDBA; run the following SQL to check for presence of\n Oracle Default Sample Schema User Accounts:\n select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\n\n If any of the users listed above is returned it means that there are demo\n programs installed, so this is a finding.\n \"\n tag \"fix\": \"Remove any demonstration and sample databases, database\n applications, objects, and files from the DBMS.\n\n To remove an account and all objects owned by that account (using BI as an\n example):\n DROP USER BI CASCADE;\n\n To remove objects without removing their owner, use the appropriate DROP\n statement (DROP TABLE, DROP VIEW, etc.).\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n sample_schema_user_accounts = sql.query(\"select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\").column('username')\n\n describe 'The list of oracle default sample schema user accounts' do\n subject { sample_schema_user_accounts }\n it { should be_empty }\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61677.rb" + }, + "waiver_data": {}, + "results": [] + } + ], + "status": "loaded" + }, + { + "name": "aws-rds-oracle-database-12c-stig-baseline", + "version": "0.1.0", + "sha256": "a34d4b2bb6d5675173abdb1df727cc552807b5c80c1d5de027b85c640f8a0fee", + "title": "InSpec Profile", + "maintainer": "The Authors", + "summary": "An InSpec Compliance Profile", + "license": "Apache-2.0", + "copyright": "The Authors", + "copyright_email": "you@example.com", + "supports": [], + "attributes": [ + { + "name": "user", + "options": { + "value": "admin1" + } + }, + { + "name": "password", + "options": { + "value": "1qaz!QAZ1qaz!QAZ" + } + }, + { + "name": "host", + "options": { + "value": "orcl.ctp9kse964go.us-east-1.rds.amazonaws.com" + } + }, + { + "name": "service", + "options": { + "value": "ORCL" + } + }, + { + "name": "sqlplus_bin", + "options": { + "value": "/usr/bin/sqlplus" + } + }, + { + "name": "standard_auditing_used", + "options": { + "type": "Boolean", + "value": true + } + }, + { + "name": "unified_auditing_used", + "options": { + "type": "Boolean", + "value": false + } + }, + { + "name": "allowed_db_links", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_dbadmin_users", + "options": { + "value": [] + } + }, + { + "name": "users_allowed_access_to_public", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_dba_role", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_system_tablespace", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_application_owners", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_unlocked_oracledb_accounts", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "users_allowed_access_to_dictionary_table", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_users_with_admin_privs", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_audit_users", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_dbaobject_owners", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_oracledb_components", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "allowed_oracledb_components_integrated_into_dbms", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "oracle_dbas", + "options": { + "type": "Array", + "value": [] + } + }, + { + "name": "emergency_profile_list", + "options": { + "value": [ + "RDSADMIN" + ] + } + } + ], + "parent_profile": "cms-ars-3.1-moderate-aws-rds-oracle-database-12c-stig-overlay", + "depends": [ + { + "name": "oracle-database-12c-stig-baseline", + "git": "https://github.com/mitre/oracle-database-12c-stig-baseline", + "branch": "issue-1112", + "status": "loaded" + } + ], + "groups": [ + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61409.rb", + "controls": [ + "V-61409" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61449.rb", + "controls": [ + "V-61449" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61867.rb", + "controls": [ + "V-61867" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61635.rb", + "controls": [ + "V-61635" + ] + }, + { + "id": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61677.rb", + "controls": [ + "V-61677" + ] + } + ], + "controls": [ + { + "id": "V-61409", + "title": "Audit trail data must be retained online for ninety (90) days and archived \n for old records for one (1) year.", + "desc": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding.", + "descriptions": [ + { + "label": "default", + "data": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding." + }, + { + "label": "check", + "data": "Develop, document and implement an audit retention policy and \n procedures.\n\n It is recommended that the most recent ninety days of audit logs remain \n available online.\n\n After thirty ninety days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to \n investigate suspicious activity." + } + ], + "impact": 0.5, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61409", + "rid": "SV-75899r1_rule", + "stig_id": "O121-BP-021100", + "fix_id": "F-67325r1_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review and verify the implementation of an audit trail\n retention policy.\n\n Verify that audit data is maintained for a minimum of one year.\n\n If audit data is not maintained for a minimum of one year, this is a finding.", + "fix": "Develop, document and implement an audit retention policy and\n procedures.\n\n It is recommended that the most recent thirty days of audit logs remain\n available online.\n\n After thirty days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to\n investigate suspicious activity." + }, + "code": "", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61409.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61449", + "title": "Database job/batch queues must be reviewed regularly to detect\n unauthorized database job submissions.", + "desc": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions.", + "descriptions": [ + { + "label": "default", + "data": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61449", + "rid": "SV-75939r3_rule", + "stig_id": "O121-BP-023100", + "fix_id": "F-67365r2_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "The DBMS_JOB PL/SQL package has been replaced by DBMS_SCHEDULER\n in Oracle versions 10.1 and higher, though it continues to be supported for\n backward compatibility.\n\n Run this query:\n select value from v$parameter where name = 'job_queue_processes';\n\n Run this query:\n select value from all_scheduler_global_attribute\n where ATTRIBUTE_NAME = 'MAX_JOB_SLAVE_PROCESSES';\n\n To understand the relationship between these settings, review:\n https://docs.oracle.com/database/121/ADMIN/appendix_a.htm#ADMIN11002\n\n Review documented and implemented procedures for monitoring the Oracle DBMS\n job/batch queues for unauthorized submissions. If procedures for job queue\n review are not defined, documented or evidence of implementation does not\n exist, this is a finding.\n\n Job queue information is available from the DBA_JOBS view. The following\n command lists jobs submitted to the queue. DBMS_JOB does not generate a\n 'history' of previous job executions.\n\n Run this query:\n select job, next_date, next_sec, failures, broken from dba_jobs;\n\n Scheduler queue information is available from the DBA_SCHEDULER_JOBS view. The\n following command lists jobs submitted to the queue.\n\n Run this query:\n select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;", + "fix": "Develop, document and implement procedures to monitor the\n database job queues for unauthorized job submissions.\n\n Develop, document and implement a formal migration plan to convert jobs using\n DBMS_JOB to use DBMS_SCHEDULER instead for Oracle versions 10.1 and higher.\n (This does not apply to DBMS_JOB jobs generated by Oracle itself, such as those\n for refreshing materialized views.)\n\n Set the value of the job_queue_processes parameter to a low value to restrict\n concurrent DBMS_JOB executions.\n\n Use auditing to capture use of the DBMS_JOB package in the audit trail. Review\n the audit trail for unauthorized use of the DBMS_JOB package." + }, + "code": "", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61449.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61867", + "title": "Database software, applications, and configuration files must be\n monitored to discover unauthorized changes.", + "desc": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations.", + "descriptions": [ + { + "label": "default", + "data": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations." + } + ], + "impact": 0, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000133-DB-000179", + "gid": "V-61867", + "rid": "SV-76357r2_rule", + "stig_id": "O121-OS-010700", + "fix_id": "F-67783r2_fix", + "cci": [ + "CCI-001499" + ], + "nist": [ + "CM-5 (6)", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review monitoring procedures and implementation evidence to\n verify that monitoring of changes to database software libraries, related\n applications, and configuration files is done.\n\n Verify that the list of files and directories being monitored is complete. If\n monitoring does not occur or is not complete, this is a finding.", + "fix": "Implement procedures to monitor for unauthorized changes to DBMS\n software libraries, related software application libraries, and configuration\n files. If a third-party automated tool is not employed, an automated job that\n reports file information on the directories and files of interest and compares\n them to the baseline report for the same will meet the requirement.\n\n File hashes or checksums should be used for comparisons since file dates may be\n manipulated by malicious users." + }, + "code": " control 'V-61867' do\n impact 0.0\n describe 'This control is not applicable on oracle within aws rds, as aws manages the operating system in which the oracle database is running on' do\n skip 'This control is not applicable on oracle within aws rds, as aws manages the operating system in which the oracle database is running on'\n end\n end\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61867.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61635", + "title": "The DBMS must produce audit records containing sufficient information\n to establish the sources (origins) of the events.", + "desc": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable.", + "descriptions": [ + { + "label": "default", + "data": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000098-DB-000042", + "gid": "V-61635", + "rid": "SV-76125r1_rule", + "stig_id": "O121-C2-007700", + "fix_id": "F-67547r1_fix", + "cci": [ + "CCI-000133" + ], + "nist": [ + "AU-3", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Verify, using vendor and system documentation if necessary,\n that the DBMS is configured to use Oracle's auditing features, or that a\n third-party product or custom code is deployed and configured to satisfy this\n requirement.\n\n If a third-party product or custom code is used, compare its current\n configuration with the audit requirements. If any of the requirements is not\n covered by the configuration, this is a finding.\n\n The remainder of this Check is applicable specifically where Oracle auditing is\n in use.\n\n If Standard Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SHOW PARAMETER AUDIT_TRAIL\n\n or the following SQL query:\n\n SELECT * FROM SYS.V$PARAMETER WHERE NAME = 'audit_trail';\n\n If Oracle returns the value 'NONE', this is a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the SYS.AUD$\n table or the audit file, whichever is in use.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\n\n If Unified Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SELECT * FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\n\n If Oracle returns the value \"TRUE\", this is not a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the\n SYS.UNIFIED_AUDIT_TRAIL view.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.", + "fix": "Configure the DBMS's auditing to audit standard and\n organization-defined auditable events, the audit record to include the source\n of the event. If preferred, use a third-party or custom tool.\n\n If using a third-party product, proceed in accordance with the product\n documentation. If using Oracle's capabilities, proceed as follows.\n\n If Standard Auditing is used:\n Use this process to ensure auditable events are captured:\n\n ALTER SYSTEM SET AUDIT_TRAIL= SCOPE=SPFILE;\n\n Audit trail type can be 'OS', 'DB', 'DB,EXTENDED', 'XML' or 'XML,EXTENDED'.\n After executing this statement, it may be necessary to shut down and restart\n the Oracle database.\n\n If Unified Auditing is used:\n To ensure auditable events are captured:\n Link the oracle binary with uniaud_on, and then restart the database.\n\n\n\n Oracle Database Upgrade Guide describes how to enable unified auditing.\n\n For more information on the configuration of auditing, refer to the following\n documents:\n \"Auditing Database Activity\" in the Oracle Database 2 Day + Security Guide:\n http://docs.oracle.com/database/121/TDPSG/tdpsg_auditing.htm#TDPSG50000\n \"Monitoring Database Activity with Auditing\" in the Oracle Database Security\n Guide:\n http://docs.oracle.com/database/121/DBSEG/part_6.htm#CCHEHCGI\n \"DBMS_AUDIT_MGMT\" in the Oracle Database PL/SQL Packages and Types Reference:\n http://docs.oracle.com/database/121/ARPLS/d_audit_mgmt.htm#ARPLS241\n Oracle Database Upgrade Guide:\n http://docs.oracle.com/database/121/UPGRD/afterup.htm#UPGRD52810" + }, + "code": "", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61635.rb" + }, + "waiver_data": {}, + "results": [] + }, + { + "id": "V-61677", + "title": "Default demonstration and sample databases, database objects, and\n applications must be removed.", + "desc": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system.", + "descriptions": [ + { + "label": "default", + "data": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000141-DB-000090", + "gid": "V-61677", + "rid": "SV-76167r3_rule", + "stig_id": "O121-C2-011500", + "fix_id": "F-67591r1_fix", + "cci": [ + "CCI-000381" + ], + "nist": [ + "CM-7 a", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "If Oracle is hosted on a server that does not support\n production systems, and is designated for the deployment of samples and\n demonstrations, this is not applicable (NA).\n\n Review documentation and websites from Oracle and any other relevant vendors\n for vendor-provided demonstration or sample databases, database applications,\n schemas, objects, and files.\n\n Review the Oracle DBMS to determine if any of the demonstration and sample\n databases, schemas, database applications, or files are installed in the\n database or are included with the DBMS application. If any are present in the\n database or are included with the DBMS application, this is a finding.\n\n The Oracle Default Sample Schema User Accounts are:\n\n BI\n Owns the Business Intelligence schema included in the Oracle Sample Schemas.\n\n HR\n Manages the Human Resources schema. Schema stores information about the\n employees and the facilities of the company.\n\n OE\n Manages the Order Entry schema. Schema stores product inventories and sales of\n the company's products through various channels.\n\n PM\n Manages the Product Media schema. Schema contains descriptions and detailed\n information about each product sold by the company.\n\n IX\n Manages the Information Exchange schema. Schema manages shipping through\n business-to-business (B2B) applications database.\n\n SH\n Manages the Sales schema. Schema stores statistics to facilitate business\n decisions.\n\n SCOTT\n A demonstration account with a simple schema.\n\n Connect to Oracle as SYSDBA; run the following SQL to check for presence of\n Oracle Default Sample Schema User Accounts:\n select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\n\n If any of the users listed above is returned it means that there are demo\n programs installed, so this is a finding.\n ", + "fix": "Remove any demonstration and sample databases, database\n applications, objects, and files from the DBMS.\n\n To remove an account and all objects owned by that account (using BI as an\n example):\n DROP USER BI CASCADE;\n\n To remove objects without removing their owner, use the appropriate DROP\n statement (DROP TABLE, DROP VIEW, etc.)." + }, + "code": "", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61677.rb" + }, + "waiver_data": {}, + "results": [] + } + ], + "status": "loaded" + }, + { + "name": "Oracle Database 12c Security Technical Implementation Guide", + "version": "0.1.0", + "sha256": "1c5163a13ada389df3dc6c58f91b4e9b79df44e2c37fadfd67904c94012aee22", + "title": "Oracle Database 12c Security Technical Implementation Guide", + "maintainer": "The Authors", + "summary": "This Security Technical Implementation Guide is published as a tool to improve the security of Department of Defense (DoD) information systems. The requirements are derived from the National Institute of Standards and Technology (NIST) 800-53 and related documents. Comments or proposed revisions to this document should be sent via e-mail to the following address: disa.stig_spt@mail.mil.", + "license": "Apache-2.0", + "copyright": "The Authors", + "copyright_email": "you@example.com", + "supports": [], + "attributes": [], + "parent_profile": "aws-rds-oracle-database-12c-stig-baseline", + "groups": [ + { + "id": "controls/V-61409.rb", + "controls": [ + "V-61409" + ] + }, + { + "id": "controls/V-61449.rb", + "controls": [ + "V-61449" + ] + }, + { + "id": "controls/V-61867.rb", + "controls": [ + "V-61867" + ] + }, + { + "id": "controls/V-61635.rb", + "controls": [ + "V-61635" + ] + }, + { + "id": "controls/V-61677.rb", + "controls": [ + "V-61677" + ] + } + ], + "controls": [ + { + "id": "V-61409", + "title": "Audit trail data must be retained online for ninety (90) days and archived \n for old records for one (1) year.", + "desc": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding.", + "descriptions": [ + { + "label": "default", + "data": "Review and verify the implementation of an audit trail retention policy.\n \n Verify that audit data is maintained online for ninety (90) days and archived for \n old records for one (1) year to provide support for after-the-fact investigations \n of security incidents.\n\n If not, this is a finding." + }, + { + "label": "check", + "data": "Develop, document and implement an audit retention policy and \n procedures.\n\n It is recommended that the most recent ninety days of audit logs remain \n available online.\n\n After thirty ninety days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to \n investigate suspicious activity." + } + ], + "impact": 0.5, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61409", + "rid": "SV-75899r1_rule", + "stig_id": "O121-BP-021100", + "fix_id": "F-67325r1_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review and verify the implementation of an audit trail\n retention policy.\n\n Verify that audit data is maintained for a minimum of one year.\n\n If audit data is not maintained for a minimum of one year, this is a finding.", + "fix": "Develop, document and implement an audit retention policy and\n procedures.\n\n It is recommended that the most recent thirty days of audit logs remain\n available online.\n\n After thirty days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to\n investigate suspicious activity." + }, + "code": "control 'V-61409' do\n title 'Audit trail data must be retained for at least one year.'\n desc \"Without preservation, a complete discovery of an attack or suspicious\n activity may not be determined. DBMS audit data also contributes to the\n complete investigation of unauthorized activity and needs to be included in\n audit retention plans and procedures.\"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000516-DB-999900'\n tag \"gid\": 'V-61409'\n tag \"rid\": 'SV-75899r1_rule'\n tag \"stig_id\": 'O121-BP-021100'\n tag \"fix_id\": 'F-67325r1_fix'\n tag \"cci\": ['CCI-000366']\n tag \"nist\": ['CM-6 b', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"Review and verify the implementation of an audit trail\n retention policy.\n\n Verify that audit data is maintained for a minimum of one year.\n\n If audit data is not maintained for a minimum of one year, this is a finding.\"\n tag \"fix\": \"Develop, document and implement an audit retention policy and\n procedures.\n\n It is recommended that the most recent thirty days of audit logs remain\n available online.\n\n After thirty days, the audit logs may be maintained off-line.\n\n Online maintenance provides for a more timely capability and inclination to\n investigate suspicious activity.\"\n describe 'A manual review is required to ensure audit trail data is retained for at least one year' do\n skip 'A manual review is required to ensure audit trail data is retained for at least one year'\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61409.rb" + }, + "waiver_data": {}, + "results": [ + { + "status": "skipped", + "code_desc": "A manual review is required to ensure audit trail data is retained for at least one year", + "run_time": 0.000008916, + "start_time": "2020-06-01T18:50:31+00:00", + "resource": "", + "skip_message": "A manual review is required to ensure audit trail data is retained for at least one year" + }, + { + "code_desc": "Manually verified status provided through attestation", + "status": "passed", + "message": "Attestation:\nStatus: passed\nExplanation: Audit logs are automatically backed up and preserved as necessary\n\nUpdated: 2099-05-02\nUpdated By: Yamilia Smith, Security\nFrequency: monthly", + "start_time": "2025-02-17T20:31:37.443Z" + } + ], + "attestation_data": { + "control_id": "V-61409", + "explanation": "Audit logs are automatically backed up and preserved as necessary", + "frequency": "monthly", + "status": "passed", + "updated": "2099-05-02", + "updated_by": "Yamilia Smith, Security" + } + }, + { + "id": "V-61449", + "title": "Database job/batch queues must be reviewed regularly to detect\n unauthorized database job submissions.", + "desc": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions.", + "descriptions": [ + { + "label": "default", + "data": "Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000516-DB-999900", + "gid": "V-61449", + "rid": "SV-75939r3_rule", + "stig_id": "O121-BP-023100", + "fix_id": "F-67365r2_fix", + "cci": [ + "CCI-000366" + ], + "nist": [ + "CM-6 b", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "The DBMS_JOB PL/SQL package has been replaced by DBMS_SCHEDULER\n in Oracle versions 10.1 and higher, though it continues to be supported for\n backward compatibility.\n\n Run this query:\n select value from v$parameter where name = 'job_queue_processes';\n\n Run this query:\n select value from all_scheduler_global_attribute\n where ATTRIBUTE_NAME = 'MAX_JOB_SLAVE_PROCESSES';\n\n To understand the relationship between these settings, review:\n https://docs.oracle.com/database/121/ADMIN/appendix_a.htm#ADMIN11002\n\n Review documented and implemented procedures for monitoring the Oracle DBMS\n job/batch queues for unauthorized submissions. If procedures for job queue\n review are not defined, documented or evidence of implementation does not\n exist, this is a finding.\n\n Job queue information is available from the DBA_JOBS view. The following\n command lists jobs submitted to the queue. DBMS_JOB does not generate a\n 'history' of previous job executions.\n\n Run this query:\n select job, next_date, next_sec, failures, broken from dba_jobs;\n\n Scheduler queue information is available from the DBA_SCHEDULER_JOBS view. The\n following command lists jobs submitted to the queue.\n\n Run this query:\n select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;", + "fix": "Develop, document and implement procedures to monitor the\n database job queues for unauthorized job submissions.\n\n Develop, document and implement a formal migration plan to convert jobs using\n DBMS_JOB to use DBMS_SCHEDULER instead for Oracle versions 10.1 and higher.\n (This does not apply to DBMS_JOB jobs generated by Oracle itself, such as those\n for refreshing materialized views.)\n\n Set the value of the job_queue_processes parameter to a low value to restrict\n concurrent DBMS_JOB executions.\n\n Use auditing to capture use of the DBMS_JOB package in the audit trail. Review\n the audit trail for unauthorized use of the DBMS_JOB package." + }, + "code": "control 'V-61449' do\n title \"Database job/batch queues must be reviewed regularly to detect\n unauthorized database job submissions.\"\n desc \"Unauthorized users may bypass security mechanisms by submitting jobs\n to job queues managed by the database to be run under a more privileged\n security context of the database or host system. These queues must be monitored\n regularly to detect any such unauthorized job submissions.\"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000516-DB-999900'\n tag \"gid\": 'V-61449'\n tag \"rid\": 'SV-75939r3_rule'\n tag \"stig_id\": 'O121-BP-023100'\n tag \"fix_id\": 'F-67365r2_fix'\n tag \"cci\": ['CCI-000366']\n tag \"nist\": ['CM-6 b', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"The DBMS_JOB PL/SQL package has been replaced by DBMS_SCHEDULER\n in Oracle versions 10.1 and higher, though it continues to be supported for\n backward compatibility.\n\n Run this query:\n select value from v$parameter where name = 'job_queue_processes';\n\n Run this query:\n select value from all_scheduler_global_attribute\n where ATTRIBUTE_NAME = 'MAX_JOB_SLAVE_PROCESSES';\n\n To understand the relationship between these settings, review:\n https://docs.oracle.com/database/121/ADMIN/appendix_a.htm#ADMIN11002\n\n Review documented and implemented procedures for monitoring the Oracle DBMS\n job/batch queues for unauthorized submissions. If procedures for job queue\n review are not defined, documented or evidence of implementation does not\n exist, this is a finding.\n\n Job queue information is available from the DBA_JOBS view. The following\n command lists jobs submitted to the queue. DBMS_JOB does not generate a\n 'history' of previous job executions.\n\n Run this query:\n select job, next_date, next_sec, failures, broken from dba_jobs;\n\n Scheduler queue information is available from the DBA_SCHEDULER_JOBS view. The\n following command lists jobs submitted to the queue.\n\n Run this query:\n select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;\"\n tag \"fix\": \"Develop, document and implement procedures to monitor the\n database job queues for unauthorized job submissions.\n\n Develop, document and implement a formal migration plan to convert jobs using\n DBMS_JOB to use DBMS_SCHEDULER instead for Oracle versions 10.1 and higher.\n (This does not apply to DBMS_JOB jobs generated by Oracle itself, such as those\n for refreshing materialized views.)\n\n Set the value of the job_queue_processes parameter to a low value to restrict\n concurrent DBMS_JOB executions.\n\n Use auditing to capture use of the DBMS_JOB package in the audit trail. Review\n the audit trail for unauthorized use of the DBMS_JOB package.\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n database_jobs = sql.query(\"select owner, job_name, state, job_class, job_type, job_action\n from dba_scheduler_jobs;\").column('job_name')\n\n describe \"You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: #{database_jobs}\" do\n skip \"You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: #{database_jobs}\"\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61449.rb" + }, + "waiver_data": {}, + "results": [ + { + "status": "skipped", + "code_desc": "You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: [\"XMLDB_NFS_CLEANUP_JOB\", \"LOAD_OPATCH_INVENTORY\", \"SM$CLEAN_AUTO_SPLIT_MERGE\", \"RSE$CLEAN_RECOVERABLE_SCRIPT\", \"FGR$AUTOPURGE_JOB\", \"BSLN_MAINTAIN_STATS_JOB\", \"DRA_REEVALUATE_OPEN_FAILURES\", \"HM_CREATE_OFFLINE_DICTIONARY\", \"ORA$AUTOTASK_CLEAN\", \"FILE_SIZE_UPD\", \"CLEANUP_ONLINE_PMO\", \"CLEANUP_TRANSIENT_PKG\", \"CLEANUP_TRANSIENT_TYPE\", \"CLEANUP_TAB_IOT_PMO\", \"CLEANUP_ONLINE_IND_BUILD\", \"CLEANUP_NON_EXIST_OBJ\", \"PMO_DEFERRED_GIDX_MAINT_JOB\", \"FILE_WATCHER\", \"PURGE_LOG\"]", + "run_time": 0.000004062, + "start_time": "2020-06-01T18:50:31+00:00", + "resource": "", + "skip_message": "You must manually review the database jobs to detect unauthorized database job submissions. The jobs to review are: [\"XMLDB_NFS_CLEANUP_JOB\", \"LOAD_OPATCH_INVENTORY\", \"SM$CLEAN_AUTO_SPLIT_MERGE\", \"RSE$CLEAN_RECOVERABLE_SCRIPT\", \"FGR$AUTOPURGE_JOB\", \"BSLN_MAINTAIN_STATS_JOB\", \"DRA_REEVALUATE_OPEN_FAILURES\", \"HM_CREATE_OFFLINE_DICTIONARY\", \"ORA$AUTOTASK_CLEAN\", \"FILE_SIZE_UPD\", \"CLEANUP_ONLINE_PMO\", \"CLEANUP_TRANSIENT_PKG\", \"CLEANUP_TRANSIENT_TYPE\", \"CLEANUP_TAB_IOT_PMO\", \"CLEANUP_ONLINE_IND_BUILD\", \"CLEANUP_NON_EXIST_OBJ\", \"PMO_DEFERRED_GIDX_MAINT_JOB\", \"FILE_WATCHER\", \"PURGE_LOG\"]" + }, + { + "code_desc": "Manually verified status provided through attestation", + "status": "passed", + "message": "Attestation:\nStatus: passed\nExplanation: Database Jobs are reviewed before they are put into production\n\nUpdated: 2099-01-02\nUpdated By: Alec Hardison, Security\nFrequency: daily", + "start_time": "2025-02-17T20:31:37.443Z" + } + ], + "attestation_data": { + "control_id": "V-61449", + "explanation": "Database Jobs are reviewed before they are put into production", + "frequency": "daily", + "status": "passed", + "updated": "2099-01-02", + "updated_by": "Alec Hardison, Security" + } + }, + { + "id": "V-61867", + "title": "Database software, applications, and configuration files must be\n monitored to discover unauthorized changes.", + "desc": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations.", + "descriptions": [ + { + "label": "default", + "data": "Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations." + } + ], + "impact": 0, + "refs": [ + { + "ref": [] + } + ], + "tags": { + "gtitle": "SRG-APP-000133-DB-000179", + "gid": "V-61867", + "rid": "SV-76357r2_rule", + "stig_id": "O121-OS-010700", + "fix_id": "F-67783r2_fix", + "cci": [ + "CCI-001499" + ], + "nist": [ + "CM-5 (6)", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Review monitoring procedures and implementation evidence to\n verify that monitoring of changes to database software libraries, related\n applications, and configuration files is done.\n\n Verify that the list of files and directories being monitored is complete. If\n monitoring does not occur or is not complete, this is a finding.", + "fix": "Implement procedures to monitor for unauthorized changes to DBMS\n software libraries, related software application libraries, and configuration\n files. If a third-party automated tool is not employed, an automated job that\n reports file information on the directories and files of interest and compares\n them to the baseline report for the same will meet the requirement.\n\n File hashes or checksums should be used for comparisons since file dates may be\n manipulated by malicious users." + }, + "code": "control 'V-61867' do\n title \"Database software, applications, and configuration files must be\n monitored to discover unauthorized changes.\"\n desc \"Any changes to the hardware, software, and/or firmware components of\n the information system and/or application can potentially have significant\n effects on the overall security of the system.\n\n If the system were to allow any user to make changes to software libraries,\n then those changes might be implemented without undergoing the appropriate\n testing and approvals that are part of a robust change management process.\n\n Accordingly, only qualified and authorized individuals shall be allowed to\n obtain access to information system components for purposes of initiating\n changes, including upgrades and modifications.\n\n Unmanaged changes that occur to the database software libraries or\n configuration can lead to unauthorized or compromised installations.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000133-DB-000179'\n tag \"gid\": 'V-61867'\n tag \"rid\": 'SV-76357r2_rule'\n tag \"stig_id\": 'O121-OS-010700'\n tag \"fix_id\": 'F-67783r2_fix'\n tag \"cci\": ['CCI-001499']\n tag \"nist\": ['CM-5 (6)', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"Review monitoring procedures and implementation evidence to\n verify that monitoring of changes to database software libraries, related\n applications, and configuration files is done.\n\n Verify that the list of files and directories being monitored is complete. If\n monitoring does not occur or is not complete, this is a finding.\"\n tag \"fix\": \"Implement procedures to monitor for unauthorized changes to DBMS\n software libraries, related software application libraries, and configuration\n files. If a third-party automated tool is not employed, an automated job that\n reports file information on the directories and files of interest and compares\n them to the baseline report for the same will meet the requirement.\n\n File hashes or checksums should be used for comparisons since file dates may be\n manipulated by malicious users.\"\n describe command('grep aide /etc/crontab /etc/cron.*/*') do\n its('stdout.strip') { should_not be_empty }\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61867.rb" + }, + "waiver_data": {}, + "results": [ + { + "status": "skipped", + "code_desc": "This control is not applicable on oracle within aws rds, as aws manages the operating system in which the oracle database is running on", + "run_time": 0.000007239, + "start_time": "2020-06-01T18:50:31+00:00", + "resource": "", + "skip_message": "This control is not applicable on oracle within aws rds, as aws manages the operating system in which the oracle database is running on" + } + ] + }, + { + "id": "V-61635", + "title": "The DBMS must produce audit records containing sufficient information\n to establish the sources (origins) of the events.", + "desc": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable.", + "descriptions": [ + { + "label": "default", + "data": "Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000098-DB-000042", + "gid": "V-61635", + "rid": "SV-76125r1_rule", + "stig_id": "O121-C2-007700", + "fix_id": "F-67547r1_fix", + "cci": [ + "CCI-000133" + ], + "nist": [ + "AU-3", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "Verify, using vendor and system documentation if necessary,\n that the DBMS is configured to use Oracle's auditing features, or that a\n third-party product or custom code is deployed and configured to satisfy this\n requirement.\n\n If a third-party product or custom code is used, compare its current\n configuration with the audit requirements. If any of the requirements is not\n covered by the configuration, this is a finding.\n\n The remainder of this Check is applicable specifically where Oracle auditing is\n in use.\n\n If Standard Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SHOW PARAMETER AUDIT_TRAIL\n\n or the following SQL query:\n\n SELECT * FROM SYS.V$PARAMETER WHERE NAME = 'audit_trail';\n\n If Oracle returns the value 'NONE', this is a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the SYS.AUD$\n table or the audit file, whichever is in use.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\n\n If Unified Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SELECT * FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\n\n If Oracle returns the value \"TRUE\", this is not a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the\n SYS.UNIFIED_AUDIT_TRAIL view.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.", + "fix": "Configure the DBMS's auditing to audit standard and\n organization-defined auditable events, the audit record to include the source\n of the event. If preferred, use a third-party or custom tool.\n\n If using a third-party product, proceed in accordance with the product\n documentation. If using Oracle's capabilities, proceed as follows.\n\n If Standard Auditing is used:\n Use this process to ensure auditable events are captured:\n\n ALTER SYSTEM SET AUDIT_TRAIL= SCOPE=SPFILE;\n\n Audit trail type can be 'OS', 'DB', 'DB,EXTENDED', 'XML' or 'XML,EXTENDED'.\n After executing this statement, it may be necessary to shut down and restart\n the Oracle database.\n\n If Unified Auditing is used:\n To ensure auditable events are captured:\n Link the oracle binary with uniaud_on, and then restart the database.\n\n\n\n Oracle Database Upgrade Guide describes how to enable unified auditing.\n\n For more information on the configuration of auditing, refer to the following\n documents:\n \"Auditing Database Activity\" in the Oracle Database 2 Day + Security Guide:\n http://docs.oracle.com/database/121/TDPSG/tdpsg_auditing.htm#TDPSG50000\n \"Monitoring Database Activity with Auditing\" in the Oracle Database Security\n Guide:\n http://docs.oracle.com/database/121/DBSEG/part_6.htm#CCHEHCGI\n \"DBMS_AUDIT_MGMT\" in the Oracle Database PL/SQL Packages and Types Reference:\n http://docs.oracle.com/database/121/ARPLS/d_audit_mgmt.htm#ARPLS241\n Oracle Database Upgrade Guide:\n http://docs.oracle.com/database/121/UPGRD/afterup.htm#UPGRD52810" + }, + "code": "control 'V-61635' do\n title \"The DBMS must produce audit records containing sufficient information\n to establish the sources (origins) of the events.\"\n desc \"Information system auditing capability is critical for accurate\n forensic analysis. Audit record content that may be necessary to satisfy the\n requirement of this control, includes, but is not limited to: timestamps,\n source and destination IP addresses, user/process identifiers, event\n descriptions, application specific events, success/fail indications, file names\n involved, access control or flow control rules invoked.\n\n Without information establishing the source of activity, the value of audit\n records from a forensics perspective is questionable.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000098-DB-000042'\n tag \"gid\": 'V-61635'\n tag \"rid\": 'SV-76125r1_rule'\n tag \"stig_id\": 'O121-C2-007700'\n tag \"fix_id\": 'F-67547r1_fix'\n tag \"cci\": ['CCI-000133']\n tag \"nist\": ['AU-3', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"Verify, using vendor and system documentation if necessary,\n that the DBMS is configured to use Oracle's auditing features, or that a\n third-party product or custom code is deployed and configured to satisfy this\n requirement.\n\n If a third-party product or custom code is used, compare its current\n configuration with the audit requirements. If any of the requirements is not\n covered by the configuration, this is a finding.\n\n The remainder of this Check is applicable specifically where Oracle auditing is\n in use.\n\n If Standard Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SHOW PARAMETER AUDIT_TRAIL\n\n or the following SQL query:\n\n SELECT * FROM SYS.V$PARAMETER WHERE NAME = 'audit_trail';\n\n If Oracle returns the value 'NONE', this is a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the SYS.AUD$\n table or the audit file, whichever is in use.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\n\n If Unified Auditing is used:\n To see if Oracle is configured to capture audit data, enter the following\n SQL*Plus command:\n\n SELECT * FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\n\n If Oracle returns the value \\\"TRUE\\\", this is not a finding.\n\n To confirm that Oracle audit is capturing sufficient information to establish\n the source of events, perform a successful auditable action and an auditable\n action that results in an SQL error, and then view the results in the\n SYS.UNIFIED_AUDIT_TRAIL view.\n\n If correct values for User ID, User Host, and Terminal are not returned when\n applicable, this is a finding.\"\n tag \"fix\": \"Configure the DBMS's auditing to audit standard and\n organization-defined auditable events, the audit record to include the source\n of the event. If preferred, use a third-party or custom tool.\n\n If using a third-party product, proceed in accordance with the product\n documentation. If using Oracle's capabilities, proceed as follows.\n\n If Standard Auditing is used:\n Use this process to ensure auditable events are captured:\n\n ALTER SYSTEM SET AUDIT_TRAIL= SCOPE=SPFILE;\n\n Audit trail type can be 'OS', 'DB', 'DB,EXTENDED', 'XML' or 'XML,EXTENDED'.\n After executing this statement, it may be necessary to shut down and restart\n the Oracle database.\n\n If Unified Auditing is used:\n To ensure auditable events are captured:\n Link the oracle binary with uniaud_on, and then restart the database.\n\n\n\n Oracle Database Upgrade Guide describes how to enable unified auditing.\n\n For more information on the configuration of auditing, refer to the following\n documents:\n \\\"Auditing Database Activity\\\" in the Oracle Database 2 Day + Security Guide:\n http://docs.oracle.com/database/121/TDPSG/tdpsg_auditing.htm#TDPSG50000\n \\\"Monitoring Database Activity with Auditing\\\" in the Oracle Database Security\n Guide:\n http://docs.oracle.com/database/121/DBSEG/part_6.htm#CCHEHCGI\n \\\"DBMS_AUDIT_MGMT\\\" in the Oracle Database PL/SQL Packages and Types Reference:\n http://docs.oracle.com/database/121/ARPLS/d_audit_mgmt.htm#ARPLS241\n Oracle Database Upgrade Guide:\n http://docs.oracle.com/database/121/UPGRD/afterup.htm#UPGRD52810\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n standard_auditing_used = input('standard_auditing_used')\n unified_auditing_used = input('unified_auditing_used')\n\n describe.one do\n describe 'Standard auditing is in use for audit purposes' do\n subject { standard_auditing_used }\n it { should be true }\n end\n\n describe 'Unified auditing is in use for audit purposes' do\n subject { unified_auditing_used }\n it { should be true }\n end\n end\n\n audit_trail = sql.query(\"select value from v$parameter where name = 'audit_trail';\").column('value')\n audit_info_captured = sql.query('SELECT * FROM UNIFIED_AUDIT_TRAIL;').column('EVENT_TIMESTAMP')\n\n if standard_auditing_used\n describe 'The oracle database audit_trail parameter' do\n subject { audit_trail }\n it { should_not cmp 'NONE' }\n end\n end\n\n unified_auditing = sql.query(\"SELECT value FROM V$OPTION WHERE PARAMETER = 'Unified Auditing';\").column('value')\n\n if unified_auditing_used\n describe 'The oracle database unified auditing parameter' do\n subject { unified_auditing }\n it { should_not cmp 'FALSE' }\n end\n\n describe 'The oracle database unified auditing events captured' do\n subject { audit_info_captured }\n it { should_not be_empty }\n end\n\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61635.rb" + }, + "waiver_data": {}, + "results": [ + { + "status": "passed", + "code_desc": "Standard auditing is in use for audit purposes is expected to equal true", + "run_time": 0.000068198, + "start_time": "2020-06-01T18:50:31+00:00" + }, + { + "status": "failed", + "code_desc": "The oracle database audit_trail parameter is expected not to cmp == \"NONE\"", + "run_time": 0.000259495, + "start_time": "2020-06-01T18:50:31+00:00", + "message": "\nexpected: NONE\n got: [\"NONE\"]\n\n(compared using `cmp` matcher)\n" + } + ] + }, + { + "id": "V-61677", + "title": "Default demonstration and sample databases, database objects, and\n applications must be removed.", + "desc": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system.", + "descriptions": [ + { + "label": "default", + "data": "Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system." + } + ], + "impact": 0.5, + "refs": [], + "tags": { + "gtitle": "SRG-APP-000141-DB-000090", + "gid": "V-61677", + "rid": "SV-76167r3_rule", + "stig_id": "O121-C2-011500", + "fix_id": "F-67591r1_fix", + "cci": [ + "CCI-000381" + ], + "nist": [ + "CM-7 a", + "Rev_4" + ], + "false_negatives": null, + "false_positives": null, + "documentable": false, + "mitigations": null, + "severity_override_guidance": false, + "potential_impacts": null, + "third_party_tools": null, + "mitigation_controls": null, + "responsibility": null, + "ia_controls": null, + "check": "If Oracle is hosted on a server that does not support\n production systems, and is designated for the deployment of samples and\n demonstrations, this is not applicable (NA).\n\n Review documentation and websites from Oracle and any other relevant vendors\n for vendor-provided demonstration or sample databases, database applications,\n schemas, objects, and files.\n\n Review the Oracle DBMS to determine if any of the demonstration and sample\n databases, schemas, database applications, or files are installed in the\n database or are included with the DBMS application. If any are present in the\n database or are included with the DBMS application, this is a finding.\n\n The Oracle Default Sample Schema User Accounts are:\n\n BI\n Owns the Business Intelligence schema included in the Oracle Sample Schemas.\n\n HR\n Manages the Human Resources schema. Schema stores information about the\n employees and the facilities of the company.\n\n OE\n Manages the Order Entry schema. Schema stores product inventories and sales of\n the company's products through various channels.\n\n PM\n Manages the Product Media schema. Schema contains descriptions and detailed\n information about each product sold by the company.\n\n IX\n Manages the Information Exchange schema. Schema manages shipping through\n business-to-business (B2B) applications database.\n\n SH\n Manages the Sales schema. Schema stores statistics to facilitate business\n decisions.\n\n SCOTT\n A demonstration account with a simple schema.\n\n Connect to Oracle as SYSDBA; run the following SQL to check for presence of\n Oracle Default Sample Schema User Accounts:\n select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\n\n If any of the users listed above is returned it means that there are demo\n programs installed, so this is a finding.\n ", + "fix": "Remove any demonstration and sample databases, database\n applications, objects, and files from the DBMS.\n\n To remove an account and all objects owned by that account (using BI as an\n example):\n DROP USER BI CASCADE;\n\n To remove objects without removing their owner, use the appropriate DROP\n statement (DROP TABLE, DROP VIEW, etc.)." + }, + "code": "control 'V-61677' do\n title \"Default demonstration and sample databases, database objects, and\n applications must be removed.\"\n desc \"Information systems are capable of providing a wide variety of\n functions and services. Some of the functions and services, provided by\n default, may not be necessary to support essential organizational operations\n (e.g., key missions, functions).\n\n It is detrimental for applications to provide, or install by default,\n functionality exceeding requirements or mission objectives. Examples include,\n but are not limited to, installing advertising software, demonstrations, or\n browser plugins not related to requirements or providing a wide array of\n functionality not required for the mission.\n\n Applications must adhere to the principles of least functionality by\n providing only essential capabilities.\n\n Demonstration and sample database objects and applications present publicly\n known attack points for malicious users. These demonstration and sample objects\n are meant to provide simple examples of coding specific functions and are not\n developed to prevent vulnerabilities from being introduced to the DBMS and host\n system.\n \"\n impact 0.5\n tag \"gtitle\": 'SRG-APP-000141-DB-000090'\n tag \"gid\": 'V-61677'\n tag \"rid\": 'SV-76167r3_rule'\n tag \"stig_id\": 'O121-C2-011500'\n tag \"fix_id\": 'F-67591r1_fix'\n tag \"cci\": ['CCI-000381']\n tag \"nist\": ['CM-7 a', 'Rev_4']\n tag \"false_negatives\": nil\n tag \"false_positives\": nil\n tag \"documentable\": false\n tag \"mitigations\": nil\n tag \"severity_override_guidance\": false\n tag \"potential_impacts\": nil\n tag \"third_party_tools\": nil\n tag \"mitigation_controls\": nil\n tag \"responsibility\": nil\n tag \"ia_controls\": nil\n tag \"check\": \"If Oracle is hosted on a server that does not support\n production systems, and is designated for the deployment of samples and\n demonstrations, this is not applicable (NA).\n\n Review documentation and websites from Oracle and any other relevant vendors\n for vendor-provided demonstration or sample databases, database applications,\n schemas, objects, and files.\n\n Review the Oracle DBMS to determine if any of the demonstration and sample\n databases, schemas, database applications, or files are installed in the\n database or are included with the DBMS application. If any are present in the\n database or are included with the DBMS application, this is a finding.\n\n The Oracle Default Sample Schema User Accounts are:\n\n BI\n Owns the Business Intelligence schema included in the Oracle Sample Schemas.\n\n HR\n Manages the Human Resources schema. Schema stores information about the\n employees and the facilities of the company.\n\n OE\n Manages the Order Entry schema. Schema stores product inventories and sales of\n the company's products through various channels.\n\n PM\n Manages the Product Media schema. Schema contains descriptions and detailed\n information about each product sold by the company.\n\n IX\n Manages the Information Exchange schema. Schema manages shipping through\n business-to-business (B2B) applications database.\n\n SH\n Manages the Sales schema. Schema stores statistics to facilitate business\n decisions.\n\n SCOTT\n A demonstration account with a simple schema.\n\n Connect to Oracle as SYSDBA; run the following SQL to check for presence of\n Oracle Default Sample Schema User Accounts:\n select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\n\n If any of the users listed above is returned it means that there are demo\n programs installed, so this is a finding.\n \"\n tag \"fix\": \"Remove any demonstration and sample databases, database\n applications, objects, and files from the DBMS.\n\n To remove an account and all objects owned by that account (using BI as an\n example):\n DROP USER BI CASCADE;\n\n To remove objects without removing their owner, use the appropriate DROP\n statement (DROP TABLE, DROP VIEW, etc.).\"\n\n sql = oracledb_session(user: input('user'), password: input('password'), host: input('host'), service: input('service'), sqlplus_bin: input('sqlplus_bin'))\n\n sample_schema_user_accounts = sql.query(\"select distinct(username) from dba_users where username in\n ('BI','HR','OE','PM','IX','SH','SCOTT');\").column('username')\n\n describe 'The list of oracle default sample schema user accounts' do\n subject { sample_schema_user_accounts }\n it { should be_empty }\n end\nend\n", + "source_location": { + "line": 1, + "ref": "/home/vagrant/.inspec/cache/03cb641d7ad530bd0bd23a784f1bd73c2dc8b899/controls/V-61677.rb" + }, + "waiver_data": {}, + "results": [ + { + "status": "passed", + "code_desc": "The list of oracle default sample schema user accounts is expected to be empty", + "run_time": 0.01266834, + "start_time": "2020-06-01T18:50:31+00:00" + } + ] + } + ], + "status": "loaded" + } + ], + "statistics": { + "duration": 0.353633098 + }, + "version": "4.18.100" +} \ No newline at end of file diff --git a/test/sample_data/attestations/triple_overlay_example-attestations.json b/test/sample_data/attestations/triple_overlay_example-attestations.json new file mode 100644 index 000000000..6363b1aeb --- /dev/null +++ b/test/sample_data/attestations/triple_overlay_example-attestations.json @@ -0,0 +1,19 @@ +[ + { + "control_id": "V-61409", + "explanation": + "Audit logs are automatically backed up and preserved as necessary", + "frequency": "monthly", + "status": "passed", + "updated": "2099-05-02", + "updated_by": "Yamilia Smith, Security" + }, + { + "control_id": "V-61449", + "explanation": "Database Jobs are reviewed before they are put into production", + "frequency": "daily", + "status": "passed", + "updated": "2099-01-02", + "updated_by": "Alec Hardison, Security" + } +] \ No newline at end of file diff --git a/test/sample_data/attestations/triple_overlay_example-attestations.yml b/test/sample_data/attestations/triple_overlay_example-attestations.yml new file mode 100644 index 000000000..6042213fc --- /dev/null +++ b/test/sample_data/attestations/triple_overlay_example-attestations.yml @@ -0,0 +1,12 @@ +- control_id: V-61409 + explanation: Audit logs are automatically backed up and preserved as necessary + frequency: monthly + status: passed + updated: 2099-05-02 + updated_by: Yamilia Smith, Security +- control_id: V-61449 + explanation: Database Jobs are reviewed before they are put into production + frequency: daily + status: passed + updated: 2099-01-02 + updated_by: Alec Hardison, Security \ No newline at end of file diff --git a/test/sample_data/trufflehog/sample_input_report/trufflehog_dup.ndjson b/test/sample_data/trufflehog/sample_input_report/trufflehog_dup.ndjson new file mode 100644 index 000000000..213c073b7 --- /dev/null +++ b/test/sample_data/trufflehog/sample_input_report/trufflehog_dup.ndjson @@ -0,0 +1,6 @@ +{"SourceMetadata":{"Data":{"Filesystem":{"file":".git/config","line":13}}},"SourceID":1,"SourceType":15,"SourceName":"trufflehog - filesystem","DetectorType":17,"DetectorName":"URI","DetectorDescription":"This detector identifies URLs with embedded credentials, which can be used to access web resources without explicit user interaction.","DecoderName":"PLAIN","Verified":false,"VerificationError":"dialing local IP addresses is not allowed","VerificationFromCache":false,"Raw":"https://gitlab-ci-token:>@gitlab.my_domain.dev","RawV2":"https://gitlab-ci-token:@gitlab.my_domain.dev/foo/bar.git","Redacted":"https://gitlab-ci-token:********@gitlab.my_domain.dev","ExtraData":null,"StructuredData":null} +{"SourceMetadata":{"Data":{"Filesystem":{"file":"github.com/jackc/pgx/v5/pgxpool/pool.go","line":297}}},"SourceID":1,"SourceType":15,"SourceName":"trufflehog - filesystem","DetectorType":968,"DetectorName":"Postgres","DetectorDescription":"Postgres connection string containing credentials","DecoderName":"PLAIN","Verified":false,"VerificationError":"lookup pg.example.com on 10.96.0.10:53: server misbehaving","VerificationFromCache":false,"Raw":"postgres://jack:secret@pg.example.com:5432","RawV2":"postgres://jack:secret@pg.example.com:5432","Redacted":"","ExtraData":{"sslmode":"verify-ca"},"StructuredData":null} +{"SourceMetadata":{"Data":{"Filesystem":{"file":"github.com/jackc/pgx/v5/pgconn/config.go","line":1}}},"SourceID":1,"SourceType":15,"SourceName":"trufflehog - filesystem","DetectorType":968,"DetectorName":"Postgres","DetectorDescription":"Postgres connection string containing credentials","DecoderName":"PLAIN","Verified":false,"VerificationError":"lookup foo.example.com:5432,bar.example.com:5432: no such host","VerificationFromCache":false,"Raw":"postgres://jack:secret@foo.example.com:5432,bar.example.com:5432:5432","RawV2":"postgres://jack:secret@foo.example.com:5432,bar.example.com:5432:5432","Redacted":"","ExtraData":{"sslmode":"\u003cunset\u003e"},"StructuredData":null} +{"SourceMetadata":{"Data":{"Filesystem":{"file":"github.com/jackc/pgx/v5/pgconn/config.go","line":171}}},"SourceID":1,"SourceType":15,"SourceName":"trufflehog - filesystem","DetectorType":968,"DetectorName":"Postgres","DetectorDescription":"Postgres connection string containing credentials","DecoderName":"PLAIN","Verified":false,"VerificationError":"lookup pg.example.com on 10.96.0.10:53: server misbehaving","VerificationFromCache":false,"Raw":"postgres://jack:secret@pg.example.com:5432","RawV2":"postgres://jack:secret@pg.example.com:5432","Redacted":"","ExtraData":{"sslmode":"verify-ca"},"StructuredData":null} +{"SourceMetadata":{"Data":{"Filesystem":{"file":"github.com/pressly/goose/v3/README.md","line":93}}},"SourceID":1,"SourceType":15,"SourceName":"trufflehog - filesystem","DetectorType":968,"DetectorName":"Postgres","DetectorDescription":"Postgres connection string containing credentials","DecoderName":"PLAIN","Verified":false,"VerificationError":"lookup qwerty.us-east-1.redshift.amazonaws.com on 10.96.0.10:53: server misbehaving","VerificationFromCache":false,"Raw":"postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439","RawV2":"postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439","Redacted":"","ExtraData":{"sslmode":"\u003cunset\u003e"},"StructuredData":null} +{"SourceMetadata":{"Data":{"Filesystem":{"file":"github.com/pressly/goose/v3/README.md","line":93}}},"SourceID":1,"SourceType":15,"SourceName":"trufflehog - filesystem","DetectorType":968,"DetectorName":"Postgres","DetectorDescription":"Postgres connection string containing credentials","DecoderName":"PLAIN","Verified":false,"VerificationError":"lookup qwerty.us-east-1.redshift.amazonaws.com on 10.96.0.10:53: server misbehaving","VerificationFromCache":false,"Raw":"postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439","RawV2":"postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439","Redacted":"","ExtraData":{"sslmode":"\u003cunset\u003e"},"StructuredData":null} diff --git a/test/sample_data/trufflehog/trufflehog-ndjson-dup-hdf.json b/test/sample_data/trufflehog/trufflehog-ndjson-dup-hdf.json new file mode 100644 index 000000000..380228502 --- /dev/null +++ b/test/sample_data/trufflehog/trufflehog-ndjson-dup-hdf.json @@ -0,0 +1,94 @@ +{ + "platform": { + "name": "Heimdall Tools", + "release": "2.11.2" + }, + "version": "2.11.2", + "statistics": {}, + "profiles": [ + { + "name": "Source ID: 1, Source Name: trufflehog - filesystem", + "title": "trufflehog - filesystem", + "supports": [], + "attributes": [], + "groups": [], + "status": "loaded", + "controls": [ + { + "tags": { + "nist": [ + "IA-5(7)" + ], + "cci": [ + "CCI-004069", + "CCI-000202", + "CCI-000203", + "CCI-002367" + ], + "severity": "medium" + }, + "refs": [], + "source_location": {}, + "title": "Found URI secret using PLAIN decoder", + "id": "URI PLAIN", + "impact": 0.5, + "results": [ + { + "status": "failed", + "code_desc": "{\n \"Data\": {\n \"Filesystem\": {\n \"file\": \".git/config\",\n \"line\": 13\n }\n }\n}", + "message": "{\n \"Verified\": false,\n \"VerificationError\": \"dialing local IP addresses is not allowed\",\n \"Raw\": \"https://gitlab-ci-token:>@gitlab.my_domain.dev\",\n \"RawV2\": \"https://gitlab-ci-token:@gitlab.my_domain.dev/foo/bar.git\",\n \"Redacted\": \"https://gitlab-ci-token:********@gitlab.my_domain.dev\"\n}", + "start_time": "" + } + ] + }, + { + "tags": { + "nist": [ + "IA-5(7)" + ], + "cci": [ + "CCI-004069", + "CCI-000202", + "CCI-000203", + "CCI-002367" + ], + "severity": "medium" + }, + "refs": [], + "source_location": {}, + "title": "Found Postgres secret using PLAIN decoder", + "id": "Postgres PLAIN", + "impact": 0.5, + "results": [ + { + "status": "failed", + "code_desc": "{\n \"Data\": {\n \"Filesystem\": {\n \"file\": \"github.com/jackc/pgx/v5/pgxpool/pool.go\",\n \"line\": 297\n }\n }\n}", + "message": "{\n \"Verified\": false,\n \"VerificationError\": \"lookup pg.example.com on 10.96.0.10:53: server misbehaving\",\n \"Raw\": \"postgres://jack:secret@pg.example.com:5432\",\n \"RawV2\": \"postgres://jack:secret@pg.example.com:5432\",\n \"ExtraData\": {\n \"sslmode\": \"verify-ca\"\n }\n}", + "start_time": "" + }, + { + "status": "failed", + "code_desc": "{\n \"Data\": {\n \"Filesystem\": {\n \"file\": \"github.com/jackc/pgx/v5/pgconn/config.go\",\n \"line\": 1\n }\n }\n}", + "message": "{\n \"Verified\": false,\n \"VerificationError\": \"lookup foo.example.com:5432,bar.example.com:5432: no such host\",\n \"Raw\": \"postgres://jack:secret@foo.example.com:5432,bar.example.com:5432:5432\",\n \"RawV2\": \"postgres://jack:secret@foo.example.com:5432,bar.example.com:5432:5432\",\n \"ExtraData\": {\n \"sslmode\": \"\"\n }\n}", + "start_time": "" + }, + { + "status": "failed", + "code_desc": "{\n \"Data\": {\n \"Filesystem\": {\n \"file\": \"github.com/jackc/pgx/v5/pgconn/config.go\",\n \"line\": 171\n }\n }\n}", + "message": "{\n \"Verified\": false,\n \"VerificationError\": \"lookup pg.example.com on 10.96.0.10:53: server misbehaving\",\n \"Raw\": \"postgres://jack:secret@pg.example.com:5432\",\n \"RawV2\": \"postgres://jack:secret@pg.example.com:5432\",\n \"ExtraData\": {\n \"sslmode\": \"verify-ca\"\n }\n}", + "start_time": "" + }, + { + "status": "failed", + "code_desc": "{\n \"Data\": {\n \"Filesystem\": {\n \"file\": \"github.com/pressly/goose/v3/README.md\",\n \"line\": 93\n }\n }\n}", + "message": "{\n \"Verified\": false,\n \"VerificationError\": \"lookup qwerty.us-east-1.redshift.amazonaws.com on 10.96.0.10:53: server misbehaving\",\n \"Raw\": \"postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439\",\n \"RawV2\": \"postgres://user:password@qwerty.us-east-1.redshift.amazonaws.com:5439\",\n \"ExtraData\": {\n \"sslmode\": \"\"\n }\n}", + "start_time": "" + } + ] + } + ], + "sha256": "ba8c9ae68b0c4d07e06e0821811f716812ea379467c1d57d3117741d471bd0af" + } + ], + "passthrough": {} +} \ No newline at end of file diff --git a/test/sample_data/xccdf/cis/CIS_AlmaLinux_OS_9_Benchmark_v2.0.0-xccdf.xml b/test/sample_data/xccdf/cis/CIS_AlmaLinux_OS_9_Benchmark_v2.0.0-xccdf.xml new file mode 100644 index 000000000..ae0d23c84 --- /dev/null +++ b/test/sample_data/xccdf/cis/CIS_AlmaLinux_OS_9_Benchmark_v2.0.0-xccdf.xml @@ -0,0 +1,30340 @@ + + interim + CIS AlmaLinux OS 9 Benchmark + + This document provides prescriptive guidance for establishing a secure configuration posture for AlmaLinux OS 9 systems running on x86_64 platforms. + This guide was developed and tested against AlmaLinux OS 9.4 + +The guidance within broadly assumes that operations are being performed as the root + user, and executed under the default Bash version for the applicable distribution. Operations performed using sudo + instead of the root + user, or executed under another shell, may produce unexpected results, or fail to make the intended changes to the system. Non-root users may not be able to access certain areas of the system, especially after remediation has been performed. It is advisable to verify root + users path integrity and the integrity of any programs being run prior to execution of commands and scripts included in this benchmark. + +The default prompt for the root + user is # +, and as such all sample commands will have # + as an additional indication that it is to be executed as root +. + +To obtain the latest version of this guide, please visit http://workbench.cisecurity.org +. If you have questions, comments, or have identified ways to improve this guide, please write us at feedback@cisecurity.org +. + + BACKGROUND. + The Center for Internet Security ("CIS") provides benchmarks, scoring tools, software, data, information, suggestions, ideas, and other services and materials from the CIS website or elsewhere ("Products") as a public service to Internet users worldwide. Recommendations contained in the Products ("Recommendations") result from a consensus-building process that involves many security experts and are generally generic in nature. The Recommendations are intended to provide helpful information to organizations attempting to evaluate or improve the security of their networks, systems, and devices. Proper use of the Recommendations requires careful analysis and adaptation to specific user requirements. The Recommendations are not in any way intended to be a "quick fix" for anyone's information security needs. + NO REPRESENTATIONS, WARRANTIES, OR COVENANTS. + CIS makes no representations, warranties, or covenants whatsoever as to (i) the positive or negative effect of the Products or the Recommendations on the operation or the security of any particular network, computer system, network device, software, hardware, or any component of any of the foregoing or (ii) the accuracy, reliability, timeliness, or completeness of the Products or the Recommendations. CIS is providing the Products and the Recommendations "as is" and "as available" without representations, warranties, or covenants of any kind. USER AGREEMENTS. + By using the Products and/or the Recommendations, I and/or my organization ("We") agree and acknowledge that: + 1. No network, system, device, hardware, software, or component can be made fully secure; + 2. We are using the Products and the Recommendations solely at our own risk; + 3. We are not compensating CIS to assume any liabilities associated with our use of the Products or the Recommendations, even risks that result from CIS's negligence or failure to perform; + 4. We have the sole responsibility to evaluate the risks and benefits of the Products and Recommendations to us and to adapt the Products and the Recommendations to our particular circumstances and requirements; + 5. Neither CIS, nor any CIS Party (defined below) has any responsibility to make any corrections, updates, upgrades, or bug fixes; or to notify us of the need for any such corrections, updates, upgrades, or bug fixes; and + 6. Neither CIS nor any CIS Party has or will have any liability to us whatsoever (whether based in contract, tort, strict liability or otherwise) for any direct, indirect, incidental, consequential, or special damages (including without limitation loss of profits, loss of sales, loss of or damage to reputation,loss of customers, loss of software, data, information or emails, loss of privacy, loss of use of any computer or other equipment, business interruption, wasted management or other staff resources or claims of any kind against us from third parties) arising out of or in any way Connected with our use of or our inability to use any of the Products or Recommendations (even if CIS has been advised of the possibility of such damages), including without limitation any liability associated with infringement of intellectual property, defects, bugs, errors, omissions, viruses, worms, backdoors, Trojan horses or other harmful items. + GRANT OF LIMITED RIGHTS. + CIS hereby grants each user the following rights, but only so long as the user complies with all of the terms of these Agreed Terms of Use: + 1. Except to the extent that we may have received additional authorization pursuant to a written agreement with CIS, each user may download, install and use each of the Products on a single computer; + 2. Each user may print one or more copies of any Product or any component of a Product that is in a .txt, .pdf, .doc, .mcw, or .rtf format, provided that all such copies are printed in full and are kept intact, including without limitation the text of this Agreed Terms of Use in its entirety. + RETENTION OF INTELLECTUAL PROPERTY RIGHTS; LIMITATIONS ON DISTRIBUTION. + The Products are protected by copyright and other intellectual property laws and by international treaties. We acknowledge and agree that we are not acquiring title to any intellectual property rights in the Products and that full title and all ownership rights to the Products will remain the exclusive property of CIS or CIS Parties. CIS reserves all rights not expressly granted to users in the preceding section entitled "Grant of limited rights." + Subject to the paragraph entitled "Special Rules" (which includes a waiver, granted to some classes of CIS Members, of certain limitations in this paragraph), and except as we may have otherwise agreed in a written agreement with CIS, we agree that we will not (i) decompile, disassemble, reverse engineer, or otherwise attempt to derive the source code for any software Product that is not already in the form of source code; (ii) distribute, redistribute, encumber, sell, rent, lease, lend, sublicense, or otherwise transfer or exploit rights to any Product or any component of a Product; (iii) post any Product or any component of a Product on any website, bulletin board, ftp server, newsgroup, or other similar mechanism or device, without regard to whether such mechanism or device is internal or external, (iv) remove or alter trademark, logo, copyright or other proprietary notices, legends, symbols or labels in any Product or any component of a Product; (v) remove these Agreed Terms of Use from, or alter these Agreed Terms of Use as they appear in, any Product or any component of a Product; (vi) use any Product or any component of a Product with any derivative works based directly on a Product or any component of a Product; (vii) use any Product or any component of a Product with other products or applications that are directly and specifically dependent on such Product or any component for any part of their functionality, or (viii) represent or claim a particular level of compliance with a CIS Benchmark, scoring tool or other Product. We will not facilitate or otherwise aid other individuals or entities in any of the activities listed in this paragraph. + We hereby agree to indemnify, defend, and hold CIS and all of its officers, directors, members, contributors, employees, authors, developers, agents, affiliates, licensors, information and service providers, software suppliers, hardware suppliers, and all other persons who aided CIS in the creation, development, or maintenance of the Products or Recommendations ("CIS Parties") harmless from and against any and all liability, losses, costs, and expenses (including attorneys' fees and court costs) incurred by CIS or any CIS Party in connection with any claim arising out of any violation by us of the preceding paragraph, including without limitation CIS's right, at our expense, to assume the exclusive defense and control of any matter subject to this indemnification, and in such case, we agree to cooperate with CIS in its defense of such claim. We further agree that all CIS Parties are third-party beneficiaries of our undertakings in these Agreed Terms of Use. SPECIAL RULES. + CIS has created and will from time to time create, special rules for its members and for other persons and organizations with which CIS has a written contractual relationship. Those special rules will override and supersede these Agreed Terms of Use with respect to the users who are covered by the special rules. + CIS hereby grants each CIS Security Consulting or Software Vendor Member and each CIS Organizational User Member, but only so long as such Member remains in good standing with CIS and complies with all of the terms of these Agreed Terms of Use, the right to distribute the Products and Recommendations within such Member's own organization, whether by manual or electronic means. Each such Member acknowledges and agrees that the foregoing grant is subject to the terms of such Member's membership arrangement with CIS and may, therefore, be modified or terminated by CIS at any time. + CHOICE OF LAW; JURISDICTION; VENUE. + We acknowledge and agree that these Agreed Terms of Use will be governed by and construed in accordance with the laws of the State of Maryland, that any action at law or in equity arising out of or relating to these Agreed Terms of Use shall be filed only in the courts located in the State of Maryland, that we hereby consent and submit to the personal jurisdiction of such courts for the purposes of litigating any such action. If any of these Agreed Terms of Use shall be determined to be unlawful, void, or for any reason unenforceable, then such terms shall be deemed severable and shall not affect the validity and enforceability of any remaining provisions. + BY USING THE PRODUCTS I(WE) ACKNOWLEDGE THAT WE HAVE READ THESE AGREED TERMS OF USE IN THEIR ENTIRETY, UNDERSTAND THEM, AND I(WE) AGREE TO BE BOUND BY THEM IN ALL RESPECTS. + + 2.0.0 + + Level 1 - Server + + Items in this profile intend to: + + be practical and prudent; + provide a clear security benefit; and + not inhibit the utility of the technology beyond acceptable means. + + This profile is intended for servers. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level 2 - Server + + This profile extends the "Level 1 - Server" profile. Items in this profile exhibit one or more of the following characteristics: + + are intended for environments or use cases where security is paramount. + acts as defense in depth measure. + may negatively inhibit the utility or performance of the technology. + + This profile is intended for servers. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level 1 - Workstation + + Items in this profile intend to: + + be practical and prudent; + provide a clear security benefit; and + not inhibit the utility of the technology beyond acceptable means. + + This profile is intended for workstations. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level 2 - Workstation + + This profile extends the "Level 1 - Workstation" profile. Items in this profile exhibit one or more of the following characteristics: + + are intended for environments or use cases where security is paramount. + acts as defense in depth measure. + may negatively inhibit the utility or performance of the technology. + + This profile is intended for workstations. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure cramfs kernel module is not available + This value is used in Rule: Ensure cramfs kernel module is not available + cramfs:fs + + + Ensure freevxfs kernel module is not available + This value is used in Rule: Ensure freevxfs kernel module is not available + freevxfs:fs + + + Ensure hfs kernel module is not available + This value is used in Rule: Ensure hfs kernel module is not available + hfs:fs + + + Ensure hfsplus kernel module is not available + This value is used in Rule: Ensure hfsplus kernel module is not available + hfsplus:fs + + + Ensure jffs2 kernel module is not available + This value is used in Rule: Ensure jffs2 kernel module is not available + jffs2:fs + + + Ensure squashfs kernel module is not available + This value is used in Rule: Ensure squashfs kernel module is not available + squashfs:fs + + + Ensure udf kernel module is not available + This value is used in Rule: Ensure udf kernel module is not available + udf:fs + + + Ensure usb-storage kernel module is not available + This value is used in Rule: Ensure usb-storage kernel module is not available + usb-storage:drivers + + + Ensure /tmp is a separate partition + This value is used in Rule: Ensure /tmp is a separate partition + /tmp + + + Ensure nodev option set on /tmp partition + This value is used in Rule: Ensure nodev option set on /tmp partition + /tmp:nodev + + + Ensure nosuid option set on /tmp partition + This value is used in Rule: Ensure nosuid option set on /tmp partition + /tmp:nosuid + + + Ensure noexec option set on /tmp partition + This value is used in Rule: Ensure noexec option set on /tmp partition + /tmp:noexec + + + Ensure /dev/shm is a separate partition + This value is used in Rule: Ensure /dev/shm is a separate partition + /dev/shm + + + Ensure nodev option set on /dev/shm partition + This value is used in Rule: Ensure nodev option set on /dev/shm partition + /dev/shm:nodev + + + Ensure nosuid option set on /dev/shm partition + This value is used in Rule: Ensure nosuid option set on /dev/shm partition + /dev/shm:nosuid + + + Ensure noexec option set on /dev/shm partition + This value is used in Rule: Ensure noexec option set on /dev/shm partition + /dev/shm:noexec + + + Ensure separate partition exists for /home + This value is used in Rule: Ensure separate partition exists for /home + /home + + + Ensure nodev option set on /home partition + This value is used in Rule: Ensure nodev option set on /home partition + /home:nodev + + + Ensure nosuid option set on /home partition + This value is used in Rule: Ensure nosuid option set on /home partition + /home:nosuid + + + Ensure separate partition exists for /var + This value is used in Rule: Ensure separate partition exists for /var + /var + + + Ensure nodev option set on /var partition + This value is used in Rule: Ensure nodev option set on /var partition + /var:nodev + + + Ensure nosuid option set on /var partition + This value is used in Rule: Ensure nosuid option set on /var partition + /var:nosuid + + + Ensure separate partition exists for /var/tmp + This value is used in Rule: Ensure separate partition exists for /var/tmp + /var/tmp + + + Ensure nodev option set on /var/tmp partition + This value is used in Rule: Ensure nodev option set on /var/tmp partition + /var/tmp:nodev + + + Ensure nosuid option set on /var/tmp partition + This value is used in Rule: Ensure nosuid option set on /var/tmp partition + /var/tmp:nosuid + + + Ensure noexec option set on /var/tmp partition + This value is used in Rule: Ensure noexec option set on /var/tmp partition + /var/tmp:noexec + + + Ensure separate partition exists for /var/log + This value is used in Rule: Ensure separate partition exists for /var/log + /var/log + + + Ensure nodev option set on /var/log partition + This value is used in Rule: Ensure nodev option set on /var/log partition + /var/log:nodev + + + Ensure nosuid option set on /var/log partition + This value is used in Rule: Ensure nosuid option set on /var/log partition + /var/log:nosuid + + + Ensure noexec option set on /var/log partition + This value is used in Rule: Ensure noexec option set on /var/log partition + /var/log:noexec + + + Ensure separate partition exists for /var/log/audit + This value is used in Rule: Ensure separate partition exists for /var/log/audit + /var/log/audit + + + Ensure nodev option set on /var/log/audit partition + This value is used in Rule: Ensure nodev option set on /var/log/audit partition + /var/log/audit:nodev + + + Ensure nosuid option set on /var/log/audit partition + This value is used in Rule: Ensure nosuid option set on /var/log/audit partition + /var/log/audit:nosuid + + + Ensure noexec option set on /var/log/audit partition + This value is used in Rule: Ensure noexec option set on /var/log/audit partition + /var/log/audit:noexec + + + Ensure SELinux is not disabled in bootloader configuration + This value is used in Rule: Ensure SELinux is not disabled in bootloader configuration + selinux=0 + + + Ensure SELinux is not disabled in bootloader configuration + This value is used in Rule: Ensure SELinux is not disabled in bootloader configuration + enforcing=0 + + + Ensure SELinux policy is configured + This value is used in Rule: Ensure SELinux policy is configured + ^Loaded\s+policy\s+name:\s+(targeted|mls)\s*(\s+#.*)?$ + + + Ensure the SELinux mode is not disabled + This value is used in Rule: Ensure the SELinux mode is not disabled + ^Current mode:\s+(enforcing|permissive)$ + + + Ensure the SELinux mode is not disabled + This value is used in Rule: Ensure the SELinux mode is not disabled + ^Mode from config file:\s+(enforcing|permissive)$ + + + Ensure the SELinux mode is enforcing + This value is used in Rule: Ensure the SELinux mode is enforcing + ^Current mode:\s+enforcing$ + + + Ensure the SELinux mode is enforcing + This value is used in Rule: Ensure the SELinux mode is enforcing + ^Mode from config file:\s+enforcing$ + + + Ensure address space layout randomization is enabled + This value is used in Rule: Ensure address space layout randomization is enabled + kernel.randomize_va_space=2 + + + Ensure ptrace_scope is restricted + This value is used in Rule: Ensure ptrace_scope is restricted + kernel.yama.ptrace_scope=1 + + + Ensure core dump backtraces are disabled + This value is used in Rule: Ensure core dump backtraces are disabled + ProcessSizeMax=0:/etc/systemd/coredump.conf + + + Ensure core dump storage is disabled + This value is used in Rule: Ensure core dump storage is disabled + Storage=none:/etc/systemd/coredump.conf + + + Ensure access to /etc/motd is configured + This value is used in Rule: Ensure access to /etc/motd is configured + /etc/motd:0133 + + + Ensure access to /etc/motd is configured + This value is used in Rule: Ensure access to /etc/motd is configured + /etc/motd/:root + + + Ensure access to /etc/motd is configured + This value is used in Rule: Ensure access to /etc/motd is configured + /etc/motd/:root + + + Ensure access to /etc/issue is configured + This value is used in Rule: Ensure access to /etc/issue is configured + /etc/issue:0133 + + + Ensure access to /etc/issue is configured + This value is used in Rule: Ensure access to /etc/issue is configured + /etc/issue/:root + + + Ensure access to /etc/issue is configured + This value is used in Rule: Ensure access to /etc/issue is configured + /etc/issue/:root + + + Ensure access to /etc/issue.net is configured + This value is used in Rule: Ensure access to /etc/issue.net is configured + /etc/issue.net:0133 + + + Ensure access to /etc/issue.net is configured + This value is used in Rule: Ensure access to /etc/issue.net is configured + /etc/issue.net/:root + + + Ensure access to /etc/issue.net is configured + This value is used in Rule: Ensure access to /etc/issue.net is configured + /etc/issue.net/:root + + + Ensure GDM screen locks when the user is idle + This value is used in Rule: Ensure GDM screen locks when the user is idle + 900:5 + + + Ensure permissions on /etc/crontab are configured + This value is used in Rule: Ensure permissions on /etc/crontab are configured + /etc/crontab:0177 + + + Ensure permissions on /etc/crontab are configured + This value is used in Rule: Ensure permissions on /etc/crontab are configured + /etc/crontab:root + + + Ensure permissions on /etc/crontab are configured + This value is used in Rule: Ensure permissions on /etc/crontab are configured + /etc/crontab:root + + + Ensure permissions on /etc/cron.hourly are configured + This value is used in Rule: Ensure permissions on /etc/cron.hourly are configured + /etc/cron.hourly/:0077 + + + Ensure permissions on /etc/cron.hourly are configured + This value is used in Rule: Ensure permissions on /etc/cron.hourly are configured + /etc/cron.hourly/:root + + + Ensure permissions on /etc/cron.hourly are configured + This value is used in Rule: Ensure permissions on /etc/cron.hourly are configured + /etc/cron.hourly/:root + + + Ensure permissions on /etc/cron.daily are configured + This value is used in Rule: Ensure permissions on /etc/cron.daily are configured + /etc/cron.daily/:0077 + + + Ensure permissions on /etc/cron.daily are configured + This value is used in Rule: Ensure permissions on /etc/cron.daily are configured + /etc/cron.daily/:root + + + Ensure permissions on /etc/cron.daily are configured + This value is used in Rule: Ensure permissions on /etc/cron.daily are configured + /etc/cron.daily/:root + + + Ensure permissions on /etc/cron.weekly are configured + This value is used in Rule: Ensure permissions on /etc/cron.weekly are configured + /etc/cron.weekly/:0077 + + + Ensure permissions on /etc/cron.weekly are configured + This value is used in Rule: Ensure permissions on /etc/cron.weekly are configured + /etc/cron.weekly/:root + + + Ensure permissions on /etc/cron.weekly are configured + This value is used in Rule: Ensure permissions on /etc/cron.weekly are configured + /etc/cron.weekly/:root + + + Ensure permissions on /etc/cron.monthly are configured + This value is used in Rule: Ensure permissions on /etc/cron.monthly are configured + /etc/cron.monthly/:0077 + + + Ensure permissions on /etc/cron.monthly are configured + This value is used in Rule: Ensure permissions on /etc/cron.monthly are configured + /etc/cron.monthly/:root + + + Ensure permissions on /etc/cron.monthly are configured + This value is used in Rule: Ensure permissions on /etc/cron.monthly are configured + /etc/cron.monthly/:root + + + Ensure permissions on /etc/cron.d are configured + This value is used in Rule: Ensure permissions on /etc/cron.d are configured + /etc/cron.d/:0077 + + + Ensure permissions on /etc/cron.d are configured + This value is used in Rule: Ensure permissions on /etc/cron.d are configured + /etc/cron.d/:root + + + Ensure permissions on /etc/cron.d are configured + This value is used in Rule: Ensure permissions on /etc/cron.d are configured + /etc/cron.d/:root + + + Ensure crontab is restricted to authorized users + This value is used in Rule: Ensure crontab is restricted to authorized users + /etc/cron.deny:0137 + + + Ensure crontab is restricted to authorized users + This value is used in Rule: Ensure crontab is restricted to authorized users + /etc/cron.deny:root + + + Ensure crontab is restricted to authorized users + This value is used in Rule: Ensure crontab is restricted to authorized users + /etc/cron.deny:root + + + Ensure crontab is restricted to authorized users + This value is used in Rule: Ensure crontab is restricted to authorized users + /etc/cron.allow:0137 + + + Ensure crontab is restricted to authorized users + This value is used in Rule: Ensure crontab is restricted to authorized users + /etc/cron.allow:root + + + Ensure crontab is restricted to authorized users + This value is used in Rule: Ensure crontab is restricted to authorized users + /etc/cron.allow:root + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.allow:0137 + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.allow:root + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.allow:(root|daemon) + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.deny:0137 + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.deny:root + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.deny:(root|daemon) + + + Ensure dccp kernel module is not available + This value is used in Rule: Ensure dccp kernel module is not available + dccp:net + + + Ensure tipc kernel module is not available + This value is used in Rule: Ensure tipc kernel module is not available + tipc:net + + + Ensure rds kernel module is not available + This value is used in Rule: Ensure rds kernel module is not available + rds:net + + + Ensure sctp kernel module is not available + This value is used in Rule: Ensure sctp kernel module is not available + sctp:net + + + Ensure ip forwarding is disabled + This value is used in Rule: Ensure ip forwarding is disabled + net.ipv4.ip_forward=0 + + + Ensure ip forwarding is disabled + This value is used in Rule: Ensure ip forwarding is disabled + net.ipv6.conf.all.forwarding=0 + + + Ensure packet redirect sending is disabled + This value is used in Rule: Ensure packet redirect sending is disabled + net.ipv4.conf.all.send_redirects=0 + + + Ensure packet redirect sending is disabled + This value is used in Rule: Ensure packet redirect sending is disabled + net.ipv4.conf.default.send_redirects=0 + + + Ensure bogus icmp responses are ignored + This value is used in Rule: Ensure bogus icmp responses are ignored + net.ipv4.icmp_ignore_bogus_error_responses=1 + + + Ensure broadcast icmp requests are ignored + This value is used in Rule: Ensure broadcast icmp requests are ignored + net.ipv4.icmp_echo_ignore_broadcasts=1 + + + Ensure icmp redirects are not accepted + This value is used in Rule: Ensure icmp redirects are not accepted + net.ipv4.conf.all.accept_redirects=0 + + + Ensure icmp redirects are not accepted + This value is used in Rule: Ensure icmp redirects are not accepted + net.ipv4.conf.default.accept_redirects=0 + + + Ensure icmp redirects are not accepted + This value is used in Rule: Ensure icmp redirects are not accepted + net.ipv6.conf.all.accept_redirects=0 + + + Ensure icmp redirects are not accepted + This value is used in Rule: Ensure icmp redirects are not accepted + net.ipv6.conf.default.accept_redirects=0 + + + Ensure secure icmp redirects are not accepted + This value is used in Rule: Ensure secure icmp redirects are not accepted + net.ipv4.conf.all.secure_redirects=0 + + + Ensure secure icmp redirects are not accepted + This value is used in Rule: Ensure secure icmp redirects are not accepted + net.ipv4.conf.default.secure_redirects=0 + + + Ensure reverse path filtering is enabled + This value is used in Rule: Ensure reverse path filtering is enabled + net.ipv4.conf.all.rp_filter=1 + + + Ensure reverse path filtering is enabled + This value is used in Rule: Ensure reverse path filtering is enabled + net.ipv4.conf.default.rp_filter=1 + + + Ensure source routed packets are not accepted + This value is used in Rule: Ensure source routed packets are not accepted + net.ipv4.conf.all.accept_source_route=0 + + + Ensure source routed packets are not accepted + This value is used in Rule: Ensure source routed packets are not accepted + net.ipv4.conf.default.accept_source_route=0 + + + Ensure source routed packets are not accepted + This value is used in Rule: Ensure source routed packets are not accepted + net.ipv6.conf.all.accept_source_route=0 + + + Ensure source routed packets are not accepted + This value is used in Rule: Ensure source routed packets are not accepted + net.ipv6.conf.default.accept_source_route=0 + + + Ensure suspicious packets are logged + This value is used in Rule: Ensure suspicious packets are logged + net.ipv4.conf.all.log_martians=1 + + + Ensure suspicious packets are logged + This value is used in Rule: Ensure suspicious packets are logged + net.ipv4.conf.default.log_martians=1 + + + Ensure tcp syn cookies is enabled + This value is used in Rule: Ensure tcp syn cookies is enabled + net.ipv4.tcp_syncookies=1 + + + Ensure ipv6 router advertisements are not accepted + This value is used in Rule: Ensure ipv6 router advertisements are not accepted + net.ipv6.conf.all.accept_ra=0 + + + Ensure ipv6 router advertisements are not accepted + This value is used in Rule: Ensure ipv6 router advertisements are not accepted + net.ipv6.conf.default.accept_ra=0 + + + Ensure nftables base chains exist + This value is used in Rule: Ensure nftables base chains exist + ^\h*\H+\h+\H+\h+hook\h+input\b(\h+.*)?$ + + + Ensure nftables base chains exist + This value is used in Rule: Ensure nftables base chains exist + ^\h*\H+\h+\H+\h+hook\h+forward\b(\h+.*)?$ + + + Ensure nftables base chains exist + This value is used in Rule: Ensure nftables base chains exist + ^\h*\H+\h+\H+\h+hook\h+output\b(\h+.*)?$ + + + Ensure nftables default deny firewall policy + This value is used in Rule: Ensure nftables default deny firewall policy + input + + + Ensure nftables default deny firewall policy + This value is used in Rule: Ensure nftables default deny firewall policy + forward + + + Ensure permissions on /etc/ssh/sshd_config are configured + This value is used in Rule: Ensure permissions on /etc/ssh/sshd_config are configured + /etc/ssh/sshd_config:0177 + + + Ensure permissions on /etc/ssh/sshd_config are configured + This value is used in Rule: Ensure permissions on /etc/ssh/sshd_config are configured + /etc/ssh/sshd_config:root + + + Ensure permissions on /etc/ssh/sshd_config are configured + This value is used in Rule: Ensure permissions on /etc/ssh/sshd_config are configured + /etc/ssh/sshd_config:root + + + Ensure permissions on /etc/ssh/sshd_config are configured + This value is used in Rule: Ensure permissions on /etc/ssh/sshd_config are configured + /etc/ssh/sshd_config.d:0177:*.conf + + + Ensure permissions on /etc/ssh/sshd_config are configured + This value is used in Rule: Ensure permissions on /etc/ssh/sshd_config are configured + /etc/ssh/sshd_config.d:root:*.conf + + + Ensure permissions on /etc/ssh/sshd_config are configured + This value is used in Rule: Ensure permissions on /etc/ssh/sshd_config are configured + /etc/ssh/sshd_config.d:root:*.conf + + + Ensure sshd Ciphers are configured + This value is used in Rule: Ensure sshd Ciphers are configured + ciphers:3des-cbc,aes128-cbc,aes192-cbc,aes256-cbc,arcfour,arcfour128,arcfour256,blowfish-cbc,cast128-cbc,rijndael-cbc@lysator\.liu\.se + + + Ensure sshd KexAlgorithms is configured + This value is used in Rule: Ensure sshd KexAlgorithms is configured + kexalgorithms:diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1 + + + Ensure sshd MACs are configured + This value is used in Rule: Ensure sshd MACs are configured + macs:hmac-md5,hmac-md5-96,hmac-ripemd160,hmac-sha1-96,umac-64@openssh\.com,hmac-md5-etm@openssh\.com,hmac-md5-96-etm@openssh\.com,hmac-ripemd160-etm@openssh\.com,hmac-sha1-96-etm@openssh\.com,umac-64-etm@openssh\.com,umac-128-etm@openssh\.com + + + Ensure sshd access is configured + This value is used in Rule: Ensure sshd access is configured + allowusers,allowgroups,denyusers,denygroups:\H+\b(\h+\H+\b)* + + + Ensure sshd Banner is configured + This value is used in Rule: Ensure sshd Banner is configured + banner:\/\H+ + + + Ensure sshd ClientAliveInterval and ClientAliveCountMax are configured + This value is used in Rule: Ensure sshd ClientAliveInterval and ClientAliveCountMax are configured + clientalivecountmax:[1-9][0-9]*m? + + + Ensure sshd ClientAliveInterval and ClientAliveCountMax are configured + This value is used in Rule: Ensure sshd ClientAliveInterval and ClientAliveCountMax are configured + ClientAliveInterval:[1-9][0-9]*m? + + + Ensure sshd DisableForwarding is enabled + This value is used in Rule: Ensure sshd DisableForwarding is enabled + disableforwarding:yes + + + Ensure sshd GSSAPIAuthentication is disabled + This value is used in Rule: Ensure sshd GSSAPIAuthentication is disabled + gssapiauthentication:no\b + + + Ensure sshd HostbasedAuthentication is disabled + This value is used in Rule: Ensure sshd HostbasedAuthentication is disabled + hostbasedauthentication:no + + + Ensure sshd IgnoreRhosts is enabled + This value is used in Rule: Ensure sshd IgnoreRhosts is enabled + ignorerhosts:yes\b + + + Ensure sshd LoginGraceTime is configured + This value is used in Rule: Ensure sshd LoginGraceTime is configured + logingracetime:\b([1-9]|[1-5][0-9]|60)\b + + + Ensure sshd LogLevel is configured + This value is used in Rule: Ensure sshd LogLevel is configured + loglevel:(VERBOSE|INFO)\b + + + Ensure sshd MaxAuthTries is configured + This value is used in Rule: Ensure sshd MaxAuthTries is configured + maxauthtries:[0-4]\b + + + Ensure sshd MaxStartups is configured + This value is used in Rule: Ensure sshd MaxStartups is configured + maxstartups:(10|[1-9])\H(30|[1-2][0-9]|[1-9])\H(60|[1-5][0-9]|[1-9])\b + + + Ensure sshd MaxSessions is configured + This value is used in Rule: Ensure sshd MaxSessions is configured + maxsessions:([1-9]|10)\b + + + Ensure sshd PermitEmptyPasswords is disabled + This value is used in Rule: Ensure sshd PermitEmptyPasswords is disabled + permitemptypasswords:no\b + + + Ensure sshd PermitRootLogin is disabled + This value is used in Rule: Ensure sshd PermitRootLogin is disabled + permitrootlogin:no\b + + + Ensure sshd PermitUserEnvironment is disabled + This value is used in Rule: Ensure sshd PermitUserEnvironment is disabled + permituserenvironment:no\b + + + Ensure sshd UsePAM is enabled + This value is used in Rule: Ensure sshd UsePAM is enabled + usepam:yes\b + + + Ensure latest version of pam is installed + This value is used in Rule: Ensure latest version of pam is installed + pam-1.5.1-19 + + + Ensure latest version of authselect is installed + This value is used in Rule: Ensure latest version of authselect is installed + authselect-1.2.6-2 + + + Ensure latest version of libpwquality is installed + This value is used in Rule: Ensure latest version of libpwquality is installed + libpwquality-1.4.4-8 + + + Ensure password expiration is configured + This value is used in Rule: Ensure password expiration is configured + pass_max_days:>:365 + + + Ensure password expiration is configured + This value is used in Rule: Ensure password expiration is configured + pass_max_days:<:1 + + + Ensure minimum password days is configured + This value is used in Rule: Ensure minimum password days is configured + pass_min_days:<:1 + + + Ensure password expiration warning days is configured + This value is used in Rule: Ensure password expiration warning days is configured + pass_warn_age:<:7 + + + Ensure inactive password lock is configured + This value is used in Rule: Ensure inactive password lock is configured + inactive:>:45 + + + Ensure inactive password lock is configured + This value is used in Rule: Ensure inactive password lock is configured + inactive:<:1 + + + Ensure root is the only UID 0 account + This value is used in Rule: Ensure root is the only UID 0 account + root:0 + + + Ensure root is the only GID 0 account + This value is used in Rule: Ensure root is the only GID 0 account + root:0 + + + Ensure group root is the only GID 0 group + This value is used in Rule: Ensure group root is the only GID 0 group + root:0 + + + Ensure default user shell timeout is configured + This value is used in Rule: Ensure default user shell timeout is configured + 900 + + + Ensure journald ForwardToSyslog is disabled + This value is used in Rule: Ensure journald ForwardToSyslog is disabled + ^ForwardToSyslog=no + + + Ensure journald Compress is configured + This value is used in Rule: Ensure journald Compress is configured + ^Compress=yes + + + Ensure journald Storage is configured + This value is used in Rule: Ensure journald Storage is configured + ^Storage=persistent + + + Ensure rsyslog is installed + This value is used in Rule: Ensure rsyslog is installed + ForwardToSyslog=yes:/etc/systemd/journald.conf + + + Ensure rsyslog service is enabled and active + This value is used in Rule: Ensure rsyslog service is enabled and active + ForwardToSyslog=yes:/etc/systemd/journald.conf + + + Ensure journald is configured to send logs to rsyslog + This value is used in Rule: Ensure journald is configured to send logs to rsyslog + ForwardToSyslog=yes:/etc/systemd/journald.conf + + + Ensure auditing for processes that start prior to auditd is enabled + This value is used in Rule: Ensure auditing for processes that start prior to auditd is enabled + audit=1 + + + Ensure audit_backlog_limit is sufficient + This value is used in Rule: Ensure audit_backlog_limit is sufficient + audit_backlog_limit=\d+ + + + Ensure changes to system administration scope (sudoers) is collected + This value is used in Rule: Ensure changes to system administration scope (sudoers) is collected + -w /etc/sudoers::-p wa + + + Ensure changes to system administration scope (sudoers) is collected + This value is used in Rule: Ensure changes to system administration scope (sudoers) is collected + -w /etc/sudoers.d::-p wa + + + Ensure actions as another user are always logged + This value is used in Rule: Ensure actions as another user are always logged + -S execve:-F arch=b32:-a (always,exit|exit,always):-C (euid!=uid|uid!=euid):-F auid!=(unset|-1|4294967295):-F arch=b64 + + + Ensure actions as another user are always logged + This value is used in Rule: Ensure actions as another user are always logged + -S execve:-F arch=b64:-a (always,exit|exit,always):-C (euid!=uid|uid!=euid):-F auid!=(unset|-1|4294967295):-F arch=b32 + + + Ensure events that modify the sudo log file are collected + This value is used in Rule: Ensure events that modify the sudo log file are collected + SUDOLOGFILE::-p wa + + + Ensure events that modify date and time information are collected + This value is used in Rule: Ensure events that modify date and time information are collected + -S adjtimex,settimeofday,clock_settime:-F arch=b32:-a (always,exit|exit,always):-F arch=b64 + + + Ensure events that modify date and time information are collected + This value is used in Rule: Ensure events that modify date and time information are collected + -S adjtimex,settimeofday,clock_settime:-F arch=b64:-a (always,exit|exit,always):-F arch=b32 + + + Ensure events that modify date and time information are collected + This value is used in Rule: Ensure events that modify date and time information are collected + -w /etc/localtime::-p wa + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -S sethostname,setdomainname:-F arch=b32:-a (always,exit|exit,always):-F arch=b64 + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -S sethostname,setdomainname:-F arch=b64:-a (always,exit|exit,always):-F arch=b32 + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -w /etc/issue::-p wa + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -w /etc/issue.net::-p wa + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -w /etc/hosts::-p wa + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -w /etc/sysconfig/network::-p wa + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -w /etc/sysconfig/network-scripts::-p wa + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -w /etc/hostname::-p wa + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -w /etc/NetworkManager::-p wa + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + -S creat,open,openat,truncate,ftruncate:-F (arch=b32|exit=-EPERM):-a always,exit:-F arch=b64:-F exit=-EACCES:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + -S creat,open,openat,truncate,ftruncate:-F (arch=b32|exit=-EACCES):-a always,exit:-F arch=b64:-F exit=-EPERM:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + -S creat,open,openat,truncate,ftruncate:-F (arch=b64|exit=-EPERM):-a always,exit:-F arch=b32:-F exit=-EACCES:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + -S creat,open,openat,truncate,ftruncate:-F (arch=b64|exit=-EACCES):-a always,exit:-F arch=b32:-F exit=-EPERM:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + -w /etc/group::-p wa + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + -w /etc/passwd::-p wa + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + -w /etc/gshadow::-p wa + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + -w /etc/shadow::-p wa + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + -w /etc/security/opasswd::-p wa + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + -w /etc/nsswitch.conf::-p wa + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + -w /etc/pam.conf::-p wa + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + -w /etc/pam.d::-p wa + + + Ensure discretionary access control permission modification events are collected + This value is used in Rule: Ensure discretionary access control permission modification events are collected + -S chmod,fchmod,fchmodat,chown,fchown,lchown,fchownat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr:-F arch=b32:-a (always,exit|exit,always):-F arch=b64:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure discretionary access control permission modification events are collected + This value is used in Rule: Ensure discretionary access control permission modification events are collected + -S chmod,fchmod,fchmodat,chown,fchown,lchown,fchownat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr:-F arch=b64:-a (always,exit|exit,always):-F arch=b32:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure successful file system mounts are collected + This value is used in Rule: Ensure successful file system mounts are collected + -S mount:-F arch=b32:-a (always,exit|exit,always):-F arch=b64:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure successful file system mounts are collected + This value is used in Rule: Ensure successful file system mounts are collected + -S mount:-F arch=b64:-a (always,exit|exit,always):-F arch=b32:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure session initiation information is collected + This value is used in Rule: Ensure session initiation information is collected + -w /var/run/utmp::-p wa + + + Ensure session initiation information is collected + This value is used in Rule: Ensure session initiation information is collected + -w /var/log/wtmp::-p wa + + + Ensure session initiation information is collected + This value is used in Rule: Ensure session initiation information is collected + -w /var/log/btmp::-p wa + + + Ensure login and logout events are collected + This value is used in Rule: Ensure login and logout events are collected + -w /var/log/lastlog::-p wa + + + Ensure login and logout events are collected + This value is used in Rule: Ensure login and logout events are collected + -w /var/run/faillock::-p wa + + + Ensure file deletion events by users are collected + This value is used in Rule: Ensure file deletion events by users are collected + -S rename,unlink,unlinkat,renameat:-F arch=b32:-a (always,exit|exit,always):-F arch=b64:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure file deletion events by users are collected + This value is used in Rule: Ensure file deletion events by users are collected + -S rename,unlink,unlinkat,renameat:-F arch=b64:-a (always,exit|exit,always):-F arch=b32:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure events that modify the system's Mandatory Access Controls are collected + This value is used in Rule: Ensure events that modify the system's Mandatory Access Controls are collected + -w /etc/selinux::-p wa + + + Ensure events that modify the system's Mandatory Access Controls are collected + This value is used in Rule: Ensure events that modify the system's Mandatory Access Controls are collected + -w /usr/share/selinux::-p wa + + + Ensure successful and unsuccessful attempts to use the chcon command are collected + This value is used in Rule: Ensure successful and unsuccessful attempts to use the chcon command are collected + -F path=/usr/bin/chcon::-a (always,exit|exit,always):-F perm=x:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure successful and unsuccessful attempts to use the setfacl command are collected + This value is used in Rule: Ensure successful and unsuccessful attempts to use the setfacl command are collected + -F path=/usr/bin/setfacl::-a (always,exit|exit,always):-F perm=x:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure successful and unsuccessful attempts to use the chacl command are collected + This value is used in Rule: Ensure successful and unsuccessful attempts to use the chacl command are collected + -F path=/usr/bin/chacl::-a (always,exit|exit,always):-F perm=x:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure successful and unsuccessful attempts to use the usermod command are collected + This value is used in Rule: Ensure successful and unsuccessful attempts to use the usermod command are collected + -F path=/usr/sbin/usermod::-a (always,exit|exit,always):-F perm=x:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure kernel module loading unloading and modification is collected + This value is used in Rule: Ensure kernel module loading unloading and modification is collected + -S create_module,init_module,delete_module,query_module,finit_module:-F arch=b32:-a (always,exit|exit,always):-F arch=b64:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure kernel module loading unloading and modification is collected + This value is used in Rule: Ensure kernel module loading unloading and modification is collected + -F path=/usr/bin/kmod::-a (always,exit|exit,always):-F perm=x:-F auid>=1000:-F auid!=(unset|-1|4294967295) + + + Ensure the audit configuration is immutable + This value is used in Rule: Ensure the audit configuration is immutable + -e 2:: + + + Ensure the audit log file directory mode is configured + This value is used in Rule: Ensure the audit log file directory mode is configured + 0027 + + + Ensure audit log files mode is configured + This value is used in Rule: Ensure audit log files mode is configured + 0137 + + + Ensure audit log files owner is configured + This value is used in Rule: Ensure audit log files owner is configured + root + + + Ensure audit log files group owner is configured + This value is used in Rule: Ensure audit log files group owner is configured + (root|adm) + + + Ensure audit configuration files mode is configured + This value is used in Rule: Ensure audit configuration files mode is configured + /etc/audit/:0137:*.conf + + + Ensure audit configuration files mode is configured + This value is used in Rule: Ensure audit configuration files mode is configured + /etc/audit/:0137:*.rules + + + Ensure audit configuration files owner is configured + This value is used in Rule: Ensure audit configuration files owner is configured + /etc/audit/:root:*.conf + + + Ensure audit configuration files owner is configured + This value is used in Rule: Ensure audit configuration files owner is configured + /etc/audit/:root:*.rules + + + Ensure audit configuration files group owner is configured + This value is used in Rule: Ensure audit configuration files group owner is configured + /etc/audit/:root:*.conf + + + Ensure audit configuration files group owner is configured + This value is used in Rule: Ensure audit configuration files group owner is configured + /etc/audit/:root:*.rules + + + Ensure audit tools mode is configured + This value is used in Rule: Ensure audit tools mode is configured + /sbin/auditctl:0022 + + + Ensure audit tools mode is configured + This value is used in Rule: Ensure audit tools mode is configured + /sbin/aureport:0022 + + + Ensure audit tools mode is configured + This value is used in Rule: Ensure audit tools mode is configured + /sbin/ausearch:0022 + + + Ensure audit tools mode is configured + This value is used in Rule: Ensure audit tools mode is configured + /sbin/autrace:0022 + + + Ensure audit tools mode is configured + This value is used in Rule: Ensure audit tools mode is configured + /sbin/auditd:0022 + + + Ensure audit tools mode is configured + This value is used in Rule: Ensure audit tools mode is configured + /sbin/augenrules:0022 + + + Ensure audit tools owner is configured + This value is used in Rule: Ensure audit tools owner is configured + /sbin/auditctl:root + + + Ensure audit tools owner is configured + This value is used in Rule: Ensure audit tools owner is configured + /sbin/aureport:root + + + Ensure audit tools owner is configured + This value is used in Rule: Ensure audit tools owner is configured + /sbin/ausearch:root + + + Ensure audit tools owner is configured + This value is used in Rule: Ensure audit tools owner is configured + /sbin/autrace:root + + + Ensure audit tools owner is configured + This value is used in Rule: Ensure audit tools owner is configured + /sbin/auditd:root + + + Ensure audit tools owner is configured + This value is used in Rule: Ensure audit tools owner is configured + /sbin/augenrules:root + + + Ensure audit tools group owner is configured + This value is used in Rule: Ensure audit tools group owner is configured + /sbin/auditctl:root + + + Ensure audit tools group owner is configured + This value is used in Rule: Ensure audit tools group owner is configured + /sbin/aureport:root + + + Ensure audit tools group owner is configured + This value is used in Rule: Ensure audit tools group owner is configured + /sbin/ausearch:root + + + Ensure audit tools group owner is configured + This value is used in Rule: Ensure audit tools group owner is configured + /sbin/autrace:root + + + Ensure audit tools group owner is configured + This value is used in Rule: Ensure audit tools group owner is configured + /sbin/auditd:root + + + Ensure audit tools group owner is configured + This value is used in Rule: Ensure audit tools group owner is configured + /sbin/augenrules:root + + + Ensure permissions on /etc/passwd are configured + This value is used in Rule: Ensure permissions on /etc/passwd are configured + /etc/passwd:0133 + + + Ensure permissions on /etc/passwd are configured + This value is used in Rule: Ensure permissions on /etc/passwd are configured + /etc/passwd:root + + + Ensure permissions on /etc/passwd are configured + This value is used in Rule: Ensure permissions on /etc/passwd are configured + /etc/passwd:root + + + Ensure permissions on /etc/passwd- are configured + This value is used in Rule: Ensure permissions on /etc/passwd- are configured + /etc/passwd-:0133 + + + Ensure permissions on /etc/passwd- are configured + This value is used in Rule: Ensure permissions on /etc/passwd- are configured + /etc/passwd-:root + + + Ensure permissions on /etc/passwd- are configured + This value is used in Rule: Ensure permissions on /etc/passwd- are configured + /etc/passwd-:root + + + Ensure permissions on /etc/group are configured + This value is used in Rule: Ensure permissions on /etc/group are configured + /etc/group:0133 + + + Ensure permissions on /etc/group are configured + This value is used in Rule: Ensure permissions on /etc/group are configured + /etc/group:root + + + Ensure permissions on /etc/group are configured + This value is used in Rule: Ensure permissions on /etc/group are configured + /etc/group:root + + + Ensure permissions on /etc/group- are configured + This value is used in Rule: Ensure permissions on /etc/group- are configured + /etc/group-:0133 + + + Ensure permissions on /etc/group- are configured + This value is used in Rule: Ensure permissions on /etc/group- are configured + /etc/group-:root + + + Ensure permissions on /etc/group- are configured + This value is used in Rule: Ensure permissions on /etc/group- are configured + /etc/group-:root + + + Ensure permissions on /etc/shadow are configured + This value is used in Rule: Ensure permissions on /etc/shadow are configured + /etc/shadow:0777 + + + Ensure permissions on /etc/shadow are configured + This value is used in Rule: Ensure permissions on /etc/shadow are configured + /etc/shadow:root + + + Ensure permissions on /etc/shadow are configured + This value is used in Rule: Ensure permissions on /etc/shadow are configured + /etc/shadow:root + + + Ensure permissions on /etc/shadow- are configured + This value is used in Rule: Ensure permissions on /etc/shadow- are configured + /etc/shadow-:0777 + + + Ensure permissions on /etc/shadow- are configured + This value is used in Rule: Ensure permissions on /etc/shadow- are configured + /etc/shadow-:root + + + Ensure permissions on /etc/shadow- are configured + This value is used in Rule: Ensure permissions on /etc/shadow- are configured + /etc/shadow-:root + + + Ensure permissions on /etc/gshadow are configured + This value is used in Rule: Ensure permissions on /etc/gshadow are configured + /etc/gshadow:0777 + + + Ensure permissions on /etc/gshadow are configured + This value is used in Rule: Ensure permissions on /etc/gshadow are configured + /etc/gshadow:root + + + Ensure permissions on /etc/gshadow are configured + This value is used in Rule: Ensure permissions on /etc/gshadow are configured + /etc/gshadow:root + + + Ensure permissions on /etc/gshadow- are configured + This value is used in Rule: Ensure permissions on /etc/gshadow- are configured + /etc/gshadow-:0777 + + + Ensure permissions on /etc/gshadow- are configured + This value is used in Rule: Ensure permissions on /etc/gshadow- are configured + /etc/gshadow-:root + + + Ensure permissions on /etc/gshadow- are configured + This value is used in Rule: Ensure permissions on /etc/gshadow- are configured + /etc/gshadow-:root + + + Ensure permissions on /etc/shells are configured + This value is used in Rule: Ensure permissions on /etc/shells are configured + /etc/shells:0133 + + + Ensure permissions on /etc/shells are configured + This value is used in Rule: Ensure permissions on /etc/shells are configured + /etc/shells:root + + + Ensure permissions on /etc/shells are configured + This value is used in Rule: Ensure permissions on /etc/shells are configured + /etc/shells:root + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd:0177 + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd:root + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd:root + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd.old:0177 + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd.old:root + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd.old:root + + + Ensure autofs services are not in use + This value is used in Rule: Ensure autofs services are not in use + enabled + + + Ensure autofs services are not in use + This value is used in Rule: Ensure autofs services are not in use + active + + + Ensure avahi daemon services are not in use + This value is used in Rule: Ensure avahi daemon services are not in use + enabled + + + Ensure avahi daemon services are not in use + This value is used in Rule: Ensure avahi daemon services are not in use + active + + + Ensure avahi daemon services are not in use + This value is used in Rule: Ensure avahi daemon services are not in use + enabled + + + Ensure avahi daemon services are not in use + This value is used in Rule: Ensure avahi daemon services are not in use + active + + + Ensure dhcp server services are not in use + This value is used in Rule: Ensure dhcp server services are not in use + enabled + + + Ensure dhcp server services are not in use + This value is used in Rule: Ensure dhcp server services are not in use + active + + + Ensure dhcp server services are not in use + This value is used in Rule: Ensure dhcp server services are not in use + enabled + + + Ensure dhcp server services are not in use + This value is used in Rule: Ensure dhcp server services are not in use + active + + + Ensure dns server services are not in use + This value is used in Rule: Ensure dns server services are not in use + enabled + + + Ensure dns server services are not in use + This value is used in Rule: Ensure dns server services are not in use + active + + + Ensure dnsmasq services are not in use + This value is used in Rule: Ensure dnsmasq services are not in use + enabled + + + Ensure dnsmasq services are not in use + This value is used in Rule: Ensure dnsmasq services are not in use + active + + + Ensure samba file server services are not in use + This value is used in Rule: Ensure samba file server services are not in use + enabled + + + Ensure samba file server services are not in use + This value is used in Rule: Ensure samba file server services are not in use + active + + + Ensure ftp server services are not in use + This value is used in Rule: Ensure ftp server services are not in use + enabled + + + Ensure ftp server services are not in use + This value is used in Rule: Ensure ftp server services are not in use + active + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + enabled + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + active + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + enabled + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + active + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + enabled + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + active + + + Ensure network file system services are not in use + This value is used in Rule: Ensure network file system services are not in use + enabled + + + Ensure network file system services are not in use + This value is used in Rule: Ensure network file system services are not in use + active + + + Ensure nis server services are not in use + This value is used in Rule: Ensure nis server services are not in use + enabled + + + Ensure nis server services are not in use + This value is used in Rule: Ensure nis server services are not in use + active + + + Ensure print server services are not in use + This value is used in Rule: Ensure print server services are not in use + enabled + + + Ensure print server services are not in use + This value is used in Rule: Ensure print server services are not in use + active + + + Ensure print server services are not in use + This value is used in Rule: Ensure print server services are not in use + enabled + + + Ensure print server services are not in use + This value is used in Rule: Ensure print server services are not in use + active + + + Ensure rpcbind services are not in use + This value is used in Rule: Ensure rpcbind services are not in use + enabled + + + Ensure rpcbind services are not in use + This value is used in Rule: Ensure rpcbind services are not in use + active + + + Ensure rpcbind services are not in use + This value is used in Rule: Ensure rpcbind services are not in use + enabled + + + Ensure rpcbind services are not in use + This value is used in Rule: Ensure rpcbind services are not in use + active + + + Ensure rsync services are not in use + This value is used in Rule: Ensure rsync services are not in use + enabled + + + Ensure rsync services are not in use + This value is used in Rule: Ensure rsync services are not in use + active + + + Ensure rsync services are not in use + This value is used in Rule: Ensure rsync services are not in use + enabled + + + Ensure rsync services are not in use + This value is used in Rule: Ensure rsync services are not in use + active + + + Ensure snmp services are not in use + This value is used in Rule: Ensure snmp services are not in use + enabled + + + Ensure snmp services are not in use + This value is used in Rule: Ensure snmp services are not in use + active + + + Ensure telnet server services are not in use + This value is used in Rule: Ensure telnet server services are not in use + enabled + + + Ensure telnet server services are not in use + This value is used in Rule: Ensure telnet server services are not in use + active + + + Ensure tftp server services are not in use + This value is used in Rule: Ensure tftp server services are not in use + enabled + + + Ensure tftp server services are not in use + This value is used in Rule: Ensure tftp server services are not in use + active + + + Ensure tftp server services are not in use + This value is used in Rule: Ensure tftp server services are not in use + enabled + + + Ensure tftp server services are not in use + This value is used in Rule: Ensure tftp server services are not in use + active + + + Ensure web proxy server services are not in use + This value is used in Rule: Ensure web proxy server services are not in use + enabled + + + Ensure web proxy server services are not in use + This value is used in Rule: Ensure web proxy server services are not in use + active + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + enabled + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + active + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + enabled + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + active + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + enabled + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + active + + + Ensure xinetd services are not in use + This value is used in Rule: Ensure xinetd services are not in use + enabled + + + Ensure xinetd services are not in use + This value is used in Rule: Ensure xinetd services are not in use + active + + + Ensure cron daemon is enabled and active + This value is used in Rule: Ensure cron daemon is enabled and active + enabled + + + Ensure cron daemon is enabled and active + This value is used in Rule: Ensure cron daemon is enabled and active + active + + + Ensure cron daemon is enabled and active + This value is used in Rule: Ensure cron daemon is enabled and active + enabled + + + Ensure cron daemon is enabled and active + This value is used in Rule: Ensure cron daemon is enabled and active + active + + + Ensure bluetooth services are not in use + This value is used in Rule: Ensure bluetooth services are not in use + enabled + + + Ensure bluetooth services are not in use + This value is used in Rule: Ensure bluetooth services are not in use + active + + + Ensure a single firewall configuration utility is in use + This value is used in Rule: Ensure a single firewall configuration utility is in use + enabled + + + Ensure a single firewall configuration utility is in use + This value is used in Rule: Ensure a single firewall configuration utility is in use + active + + + Ensure a single firewall configuration utility is in use + This value is used in Rule: Ensure a single firewall configuration utility is in use + enabled + + + Ensure a single firewall configuration utility is in use + This value is used in Rule: Ensure a single firewall configuration utility is in use + inactive + + + Ensure a single firewall configuration utility is in use + This value is used in Rule: Ensure a single firewall configuration utility is in use + enabled + + + Ensure a single firewall configuration utility is in use + This value is used in Rule: Ensure a single firewall configuration utility is in use + active + + + Ensure a single firewall configuration utility is in use + This value is used in Rule: Ensure a single firewall configuration utility is in use + enabled + + + Ensure a single firewall configuration utility is in use + This value is used in Rule: Ensure a single firewall configuration utility is in use + inactive + + + Ensure nftables base chains exist + This value is used in Rule: Ensure nftables base chains exist + enabled + + + Ensure nftables base chains exist + This value is used in Rule: Ensure nftables base chains exist + active + + + Ensure nftables default deny firewall policy + This value is used in Rule: Ensure nftables default deny firewall policy + enabled + + + Ensure nftables default deny firewall policy + This value is used in Rule: Ensure nftables default deny firewall policy + active + + + Ensure filesystem integrity is regularly checked + This value is used in Rule: Ensure filesystem integrity is regularly checked + enabled + + + Ensure filesystem integrity is regularly checked + This value is used in Rule: Ensure filesystem integrity is regularly checked + enabled + + + Ensure filesystem integrity is regularly checked + This value is used in Rule: Ensure filesystem integrity is regularly checked + aidecheck.service + + + Ensure journald service is enabled and active + This value is used in Rule: Ensure journald service is enabled and active + static + + + Ensure journald service is enabled and active + This value is used in Rule: Ensure journald service is enabled and active + active + + + Ensure systemd-journal-remote is installed + This value is used in Rule: Ensure systemd-journal-remote is installed + enabled + + + Ensure systemd-journal-remote is installed + This value is used in Rule: Ensure systemd-journal-remote is installed + active + + + Ensure systemd-journal-upload is enabled and active + This value is used in Rule: Ensure systemd-journal-upload is enabled and active + enabled + + + Ensure systemd-journal-upload is enabled and active + This value is used in Rule: Ensure systemd-journal-upload is enabled and active + active + + + Ensure systemd-journal-upload is enabled and active + This value is used in Rule: Ensure systemd-journal-upload is enabled and active + active + + + Ensure systemd-journal-upload is enabled and active + This value is used in Rule: Ensure systemd-journal-upload is enabled and active + enabled + + + Ensure systemd-journal-remote service is not in use + This value is used in Rule: Ensure systemd-journal-remote service is not in use + enabled + + + Ensure systemd-journal-remote service is not in use + This value is used in Rule: Ensure systemd-journal-remote service is not in use + active + + + Ensure systemd-journal-remote service is not in use + This value is used in Rule: Ensure systemd-journal-remote service is not in use + enabled + + + Ensure systemd-journal-remote service is not in use + This value is used in Rule: Ensure systemd-journal-remote service is not in use + active + + + Ensure systemd-journal-remote service is not in use + This value is used in Rule: Ensure systemd-journal-remote service is not in use + enabled + + + Ensure systemd-journal-remote service is not in use + This value is used in Rule: Ensure systemd-journal-remote service is not in use + active + + + Ensure journald ForwardToSyslog is disabled + This value is used in Rule: Ensure journald ForwardToSyslog is disabled + enabled + + + Ensure journald ForwardToSyslog is disabled + This value is used in Rule: Ensure journald ForwardToSyslog is disabled + active + + + Ensure journald Compress is configured + This value is used in Rule: Ensure journald Compress is configured + enabled + + + Ensure journald Compress is configured + This value is used in Rule: Ensure journald Compress is configured + active + + + Ensure journald Storage is configured + This value is used in Rule: Ensure journald Storage is configured + enabled + + + Ensure journald Storage is configured + This value is used in Rule: Ensure journald Storage is configured + active + + + Ensure rsyslog service is enabled and active + This value is used in Rule: Ensure rsyslog service is enabled and active + enabled + + + Ensure rsyslog service is enabled and active + This value is used in Rule: Ensure rsyslog service is enabled and active + active + + + Ensure auditd service is enabled and active + This value is used in Rule: Ensure auditd service is enabled and active + enabled + + + Ensure auditd service is enabled and active + This value is used in Rule: Ensure auditd service is enabled and active + active + + + Ensure root account access is controlled + This value is used in Rule: Ensure root account access is controlled + ^\!?\$.+\$ + + + Ensure root account access is controlled + This value is used in Rule: Ensure root account access is controlled + ^\! + + + Initial Setup + + Items in this section are advised for all systems, but may be difficult or require extensive preparation after the initial setup of the system. + + + Filesystem + + The file system is generally a built-in layer used to handle the data management of the storage. + + + Configure Filesystem Kernel Modules + + A number of uncommon filesystem types are supported under Linux. Removing support for unneeded filesystem types reduces the local attack surface of the system. If a filesystem type is not needed it should be disabled. Native Linux file systems are designed to ensure that built-in security controls function as expected. Non-native filesystems can lead to unexpected consequences to both the security and functionality of the system and should be used with caution. Many filesystems are created for niche use cases and are not maintained and supported as the operating systems are updated and patched. Users of non-native filesystems should ensure that there is attention and ongoing support for them, especially in light of frequent operating system changes. + Standard network connectivity and Internet access to cloud storage may make the use of non-standard filesystem formats to directly attach heterogeneous devices much less attractive. + + Note +: This should not be considered a comprehensive list of filesystems. You may wish to consider additions to those listed here for your environment. For the current available file system modules on the system see /usr/lib/modules/$(uname -r)/kernel/fs + + + Start up scripts + + +Kernel modules loaded directly via insmod + will ignore what is configured in the relevant /etc/modprobe.d/*.conf + files. If modules are still being loaded after a reboot whilst having the correctly configured blacklist + and install + command, check for insmod + entries in start up scripts such as .bashrc +. + +You may also want to check /lib/modprobe.d/ +. Please note that this directory should not be used for user defined module loading. Ensure that all such entries resides in /etc/modprobe.d/*.conf + files. + + Return values + + +Using /bin/false + as the command in disabling a particular module serves two purposes; to convey the meaning of the entry to the user and cause a non-zero return value. The latter can be tested for in scripts. Please note that insmod + will ignore what is configured in the relevant /etc/modprobe.d/*.conf + files. The preferred way to load modules is with modprobe +. + + + Ensure cramfs kernel module is not available + + +The cramfs + filesystem type is a compressed read-only Linux filesystem embedded in small footprint systems. A cramfs + image can be used without having to first decompress the image. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + STIG Finding ID: V-230498 + + + + +Run the following script to unload and disable the cramfs + module: + + - IF - + the cramfs + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install cramfs /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist cramfs + in the /etc/modprobe.d/ + directory + +Run modprobe -r cramfs 2>/dev/null; rmmod cramfs 2>/dev/null + to remove cramfs + from the kernel + + + - IF - + the cramfs + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="cramfs" # set module name
+ l_mod_type="fs" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure freevxfs kernel module is not available + + +The freevxfs + filesystem type is a free version of the Veritas type filesystem. This is the primary filesystem type for HP-UX operating systems. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to unload and disable the freevxfs + module: + + - IF - + the freevxfs + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install freevxfs /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist freevxfs + in the /etc/modprobe.d/ + directory + +Run modprobe -r freevxfs 2>/dev/null; rmmod freevxfs 2>/dev/null + to remove freevxfs + from the kernel + + + - IF - + the freevxfs + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="freevxfs" # set module name
+ l_mod_type="fs" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure hfs kernel module is not available + + +The hfs + filesystem type is a hierarchical filesystem that allows you to mount Mac OS filesystems. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to unload and disable the hfs + module: + + - IF - + the hfs + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install hfs /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist hfs + in the /etc/modprobe.d/ + directory + +Run modprobe -r hfs 2>/dev/null; rmmod hfs 2>/dev/null + to remove hfs + from the kernel + + + - IF - + the hfs + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="hfs" # set module name
+ l_mod_type="fs" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure hfsplus kernel module is not available + + +The hfsplus + filesystem type is a hierarchical filesystem designed to replace hfs + that allows you to mount Mac OS filesystems. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to unload and disable the hfsplus + module: + + - IF - + the hfsplus + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install hfsplus /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist hfsplus + in the /etc/modprobe.d/ + directory + +Run modprobe -r hfsplus 2>/dev/null; rmmod hfsplus 2>/dev/null + to remove hfsplus + from the kernel + + + - IF - + the hfsplus + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="hfsplus" # set module name
+ l_mod_type="fs" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure jffs2 kernel module is not available + + +The jffs2 + (journaling flash filesystem 2) filesystem type is a log-structured filesystem used in flash memory devices. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to unload and disable the jffs2 + module: + + - IF - + the jffs2 + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install jffs2 /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist jffs2 + in the /etc/modprobe.d/ + directory + +Run modprobe -r jffs2 2>/dev/null; rmmod jffs2 2>/dev/null + to remove jffs2 + from the kernel + + + - IF - + the jffs2 + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="jffs2" # set module name
+ l_mod_type="fs" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure squashfs kernel module is not available + + +The squashfs + filesystem type is a compressed read-only Linux filesystem embedded in small footprint systems. A squashfs + image can be used without having to first decompress the image. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + LRI: + LAI: <LRI>:<Specific ID> + + + + +Run the following script to unload and disable the udf + module: + + - IF - + the squashfs + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install squashfs /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist squashfs + in the /etc/modprobe.d/ + directory + +Run modprobe -r squashfs 2>/dev/null; rmmod squashfs 2>/dev/null + to remove squashfs + from the kernel + + + - IF - + the squashfs + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="squashfs" # set module name
+ l_mod_type="fs" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+ Impact: + + +As Snap packages utilize squashfs + as a compressed filesystem, disabling squashfs + will cause Snap packages to fail. + + Snap + application packages of software are self-contained and work across a range of Linux distributions. This is unlike traditional Linux package management approaches, like APT or RPM, which require specifically adapted packages per Linux distribution on an application update and delay therefore application deployment from developers to their software's end-user. Snaps themselves have no dependency on any external store ("App store"), can be obtained from any source and can be therefore used for upstream software deployment. + +
+
+
+ + + + + + + +
+ + Ensure udf kernel module is not available + + +The udf + filesystem type is the universal disk format used to implement ISO/IEC 13346 and ECMA-167 specifications. This is an open vendor filesystem type for data storage on a broad range of media. This filesystem type is necessary to support writing DVDs and newer optical disc formats. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to unload and disable the udf + module: + + - IF - + the udf + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install udf /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist udf + in the /etc/modprobe.d/ + directory + +Run modprobe -r udf 2>/dev/null; rmmod udf 2>/dev/null + to remove udf + from the kernel + + + - IF - + the udf + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="udf" # set module name
+ l_mod_type="fs" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+ Impact: + + +Microsoft Azure requires the usage of udf +. + + udf + should not + be disabled on systems run on Microsoft Azure. + +
+
+
+ + + + + + + +
+ + Ensure usb-storage kernel module is not available + + USB storage provides a means to transfer and store files ensuring persistence and availability of the files independent of network connection status. Its popularity and utility has led to USB-based malware being a simple and common means for network infiltration and a first step to establishing a persistent threat within a networked environment. + + + + + + + Devices + Protect + + + + + + Data + Protect + + + + + An alternative solution to disabling the usb-storage module may be found in USBGuard. + Use of USBGuard and construction of USB device policies should be done in alignment with site policy. + + + + Restricting USB access on the system will decrease the physical attack surface for a device and diminish the possible vectors to introduce malware. + + + + NIST SP 800-53 Rev. 5: SI-3 + + + + +Run the following script to unload and disable the usb-storage + module: + + - IF - + the usb-storage + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install usb-storage /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist usb-storage + in the /etc/modprobe.d/ + directory + +Run modprobe -r usb-storage 2>/dev/null; rmmod usb-storage 2>/dev/null + to remove usb-storage + from the kernel + + + - IF - + the usb-storage + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="usb-storage" # set module name
+ l_mod_type="drivers" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+ Impact: + + +Disabling the usb-storage + module will disable any usage of USB storage devices. + +If requirements and local site policy allow the use of such devices, other solutions should be configured accordingly instead. One example of a commonly used solution is USBGuard +. + +
+
+
+ + + + + + + +
+ + Ensure unused filesystems kernel modules are not available + + Filesystem kernel modules are pieces of code that can be dynamically loaded into the Linux kernel to extend its filesystem capabilities, or so-called base kernel, of an operating system. Filesystem kernel modules are typically used to add support for new hardware (as device drivers), or for adding system calls. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + While loadable filesystem kernel modules are a convenient method of modifying the running kernel, this can be abused by attackers on a compromised system to prevent detection of their processes or files, allowing them to maintain control over the system. Many rootkits make use of loadable filesystem kernel modules in this way. + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. The following filesystem kernel modules have known CVE's and should be made unavailable if no dependencies exist: + + + afs + - CVE-2022-37402 + + ceph + - CVE-2022-0670 + + cifs + - CVE-2022-29869 + + exfat + CVE-2022-29973 + + ext + CVE-2022-1184 + + fat + CVE-2022-22043 + + fscache + CVE-2022-3630 + + fuse + CVE-2023-0386 + + gfs2 + CVE-2023-3212 + + nfs_common + CVE-2023-6660 + + nfsd + CVE-2022-43945 + + smbfs_common + CVE-2022-2585 + + + + + https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=filesystem + + + + + - IF - + the module is available in the running kernel: + + Unload the filesystem kernel module from the kernel + +Create a file ending in .conf + with install filesystem kernel modules /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with deny list filesystem kernel modules in the /etc/modprobe.d/ + directory + + + WARNING +: unloading, disabling or denylisting filesystem modules that are in use on the system maybe FATAL. It is extremely important to thoroughly review the filesystems returned by the audit before following the remediation procedure. + + +Example of unloading the gfs2 +kernel module: + + +# modprobe -r gfs2 2>/dev/null
+# rmmod gfs2 2>/dev/null +
+ + +Example of fully disabling the gfs2 + kernel module: + + # printf '%s\n' "blacklist gfs2" "install gfs2 /bin/false" >> /etc/modprobe.d/gfs2.conf + + + Note: + + + Disabling a kernel module by modifying the command above for each unused filesystem kernel module + +The example gfs2 + must be updated with the appropriate module name for the command or example script bellow to run correctly. + + + Below is an example Script that can be modified to use on various filesystem kernel modules manual remediation process: + + + Example Script + + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="gfs2" # set module name
+ l_mod_type="fs" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+ Impact: + + This list may be quite extensive and covering all edges cases is difficult. Therefore, it's crucial to carefully consider the implications and dependencies before making any changes to the filesystem kernel module configurations. + +
+
+
+
+
+ + Configure Filesystem Partitions + + Directories that are used for system-wide functions can be further protected by placing them on separate partitions. This provides protection for resource exhaustion and enables the use of mounting options that are applicable to the directory's intended use. Users' data can be stored on separate partitions and have stricter mount options. A user partition is a filesystem that has been established for use by the users and does not contain software for system operations. + The recommendations in this section are easier to perform during initial system installation. If the system is already installed, it is recommended that a full backup be performed before repartitioning the system. + + Note: + + + -IF- + you are repartitioning a system that has already been installed (This may require the system to be in single-user mode): + + +Mount the new partition to a temporary mountpoint e.g. mount /dev/sda2 /mnt + + +Copy data from the original partition to the new partition. e.g. cp -a /var/tmp/* /mnt + + +Verify that all data is present on the new partition. e.g. ls -la /mnt + + +Unmount the new partition. e.g. umount /mnt + + +Remove the data from the original directory that was in the old partition. e.g. rm -Rf /var/tmp/* + Otherwise it will still consume space in the old partition that will be masked when the new filesystem is mounted. + +Mount the new partition to the desired mountpoint. e.g. mount /dev/sda2 /var/tmp + + +Update /etc/fstab + with the new mountpoint. e.g. /dev/sda2 /var/tmp xfs defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + + + + Configure /tmp + + +The /tmp + directory is a world-writable directory used to store data used by the system and user applications for a short period of time. This data should have no expectation of surviving a reboot, as this directory is intended to be emptied after each reboot. + + + Ensure /tmp is a separate partition + + +The /tmp + directory is a world-writable directory used for temporary storage by all users and some applications. + + - IF - + an entry for /tmp + exists in /etc/fstab + it will take precedence over entries in systemd default unit file. + + Note: + In an environment where the main system is diskless and connected to iSCSI, entries in /etc/fstab + may not take precedence. + + /tmp + can be configured to use tmpfs +. + + tmpfs + puts everything into the kernel internal caches and grows and shrinks to accommodate the files it contains and is able to swap unneeded pages out to swap space. It has maximum size limits which can be adjusted on the fly via mount -o remount +. + +Since tmpfs + lives completely in the page cache and on swap, all tmpfs + pages will be shown as "Shmem" in /proc/meminfo + and "Shared" in free +. Notice that these counters also include shared memory. The most reliable way to get the count is using df + and du +. + + tmpfs + has three mount options for sizing: + + + size +: The limit of allocated bytes for this tmpfs + instance. The default is half of your physical RAM without swap. If you oversize your tmpfs + instances the machine will deadlock since the OOM handler will not be able to free that memory. + + nr_blocks +: The same as size, but in blocks of PAGE_SIZE. + + nr_inodes +: The maximum number of inodes for this instance. The default is half of the number of your physical RAM pages, or (on a machine with highmem) the number of lowmem RAM pages, whichever is the lower. + + +These parameters accept a suffix k, m or g and can be changed on remount. The size parameter also accepts a suffix % to limit this tmpfs + instance to that percentage of your physical RAM. The default, when neither size + nor nr_blocks + is specified, is size=50% +. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Making /tmp + its own file system allows an administrator to set additional mount options such as the noexec + option on the mount, making /tmp + useless for an attacker to install executable code. It would also prevent an attacker from establishing a hard link to a system setuid + program and wait for it to be updated. Once the program was updated, the hard link would be broken, and the attacker would have his own copy of the program. If the program happened to have a security vulnerability, the attacker could continue to exploit the known flaw. + +This can be accomplished by either mounting tmpfs + to /tmp +, or creating a separate partition for /tmp +. + + + + https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems/ + https://www.freedesktop.org/software/systemd/man/systemd-fstab-generator.html + https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt + NIST SP 800-53 Rev. 5: CM-7 + + + + +First ensure that systemd is correctly configured to ensure that /tmp + will be mounted at boot time. + # systemctl unmask tmp.mount + + +For specific configuration requirements of the /tmp + mount for your environment, modify /etc/fstab +. + +Example of using tmpfs + with specific mount options: + tmpfs /tmp tmpfs defaults,rw,nosuid,nodev,noexec,relatime,size=2G 0 0 + + + Note: + the size=2G + is an example of setting a specific size for tmpfs +. + Example of using a volume or disk with specific mount options. The source location of the volume or disk will vary depending on your environment: + <device> /tmp <fstype> defaults,nodev,nosuid,noexec 0 0 + + Impact: + + +By design files saved to /tmp + should have no expectation of surviving a reboot of the system. tmpfs + is ram based and all files stored to tmpfs + will be lost when the system is rebooted. + +If files need to be persistent through a reboot, they should be saved to /var/tmp + not /tmp +. + +Since the /tmp + directory is intended to be world-writable, there is a risk of resource exhaustion if it is not bound to tmpfs + or a separate partition. + +Running out of /tmp + space is a problem regardless of what kind of filesystem lies under it, but in a configuration where /tmp + is not a separate file system it will essentially have the whole disk available, as the default installation only creates a single / + partition. On the other hand, a RAM-based /tmp + (as with tmpfs +) will almost certainly be much smaller, which can lead to applications filling up the filesystem much more easily. Another alternative is to create a dedicated partition for /tmp + from a separate volume or disk. One of the downsides of a disk-based dedicated partition is that it will be slower than tmpfs + which is RAM-based. + + + + + + + + + + + + + + Ensure nodev option set on /tmp partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Since the /tmp + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: CM-7 + + + + + - IF - + a separate partition exists for /tmp +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /tmp + partition. + + Example: + + <device> /tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /tmp + with the configured options: + # mount -o remount /tmp + + + + + + + + + + + + + + Ensure nosuid option set on /tmp partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /tmp + filesystem is only intended for temporary file storage, set this option to ensure that users cannot create setuid + files in /tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /tmp +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /tmp + partition. + + Example: + + <device> /tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /tmp + with the configured options: + # mount -o remount /tmp + + + + + + + + + + + + + + Ensure noexec option set on /tmp partition + + +The noexec + mount option specifies that the filesystem cannot contain executable binaries. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /tmp + filesystem is only intended for temporary file storage, set this option to ensure that users cannot run executable binaries from /tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /tmp +. + +Edit the /etc/fstab + file and add noexec + to the fourth field (mounting options) for the /tmp + partition. + + Example: + + <device> /tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /tmp + with the configured options: + # mount -o remount /tmp + + Impact: + + +Setting the noexec + option on /tmp + may prevent installation and/or updating of some 3rd party software. + + + + + + + + + + + + + + + Configure /dev/shm + + +The /dev/shm + directory is a world-writable directory that can function as shared memory that facilitates inter process communication (IPC) + + + Ensure /dev/shm is a separate partition + + +The /dev/shm + directory is a world-writable directory that can function as shared memory that facilitates inter process communication (IPC). + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Making /dev/shm + its own file system allows an administrator to set additional mount options such as the noexec + option on the mount, making /dev/shm + useless for an attacker to install executable code. It would also prevent an attacker from establishing a hard link to a system setuid + program and wait for it to be updated. Once the program was updated, the hard link would be broken and the attacker would have his own copy of the program. If the program happened to have a security vulnerability, the attacker could continue to exploit the known flaw. + +This can be accomplished by mounting tmpfs + to /dev/shm +. + + + + https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems/ + https://www.freedesktop.org/software/systemd/man/systemd-fstab-generator.html + NIST SP 800-53 Rev. 5: CM-7 + + + + +For specific configuration requirements of the /dev/shm + mount for your environment, modify /etc/fstab +. + + Example: + + tmpfs /dev/shm tmpfs defaults,rw,nosuid,nodev,noexec,relatime,size=2G 0 0 + + Impact: + + +Since the /dev/shm + directory is intended to be world-writable, there is a risk of resource exhaustion if it is not bound to a separate partition. + + /dev/shm + utilizing tmpfs + can be resized using the size={size} + parameter in the relevant entry in /etc/fstab +. + + + + + + + + + + + + + + Ensure nodev option set on /dev/shm partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +Some distributions mount /dev/shm + through other means and require /dev/shm + to be added to /etc/fstab + even though it is already being mounted on boot. Others may configure /dev/shm + in other locations and may override /etc/fstab + configuration. Consult the documentation appropriate for your distribution. + + + + +Since the /dev/shm + filesystem is not intended to support devices, set this option to ensure that users cannot attempt to create special devices in /dev/shm + partitions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /dev/shm +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /dev/shm + partition. See the fstab(5) + manual page for more information. + + Example: + + tmpfs /dev/shm tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /dev/shm + with the configured options: + # mount -o remount /dev/shm + + + Note: + It is recommended to use tmpfs + as the device/filesystem type as /dev/shm + is used as shared memory space by applications. + + + + + + + + + + + + + Ensure nosuid option set on /dev/shm partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +Some distributions mount /dev/shm + through other means and require /dev/shm + to be added to /etc/fstab + even though it is already being mounted on boot. Others may configure /dev/shm + in other locations and may override /etc/fstab + configuration. Consult the documentation appropriate for your distribution. + + + + Setting this option on a file system prevents users from introducing privileged programs onto the system and allowing non-root users to execute them. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /dev/shm +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /dev/shm + partition. See the fstab(5) + manual page for more information. + + Example: + + tmpfs /dev/shm tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /dev/shm + with the configured options: + # mount -o remount /dev/shm + + + Note: + It is recommended to use tmpfs + as the device/filesystem type as /dev/shm + is used as shared memory space by applications. + + + + + + + + + + + + + Ensure noexec option set on /dev/shm partition + + +The noexec + mount option specifies that the filesystem cannot contain executable binaries. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Setting this option on a file system prevents users from executing programs from shared memory. This deters users from introducing potentially malicious software on the system. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /dev/shm +. + +Edit the /etc/fstab + file and add noexec + to the fourth field (mounting options) for the /dev/shm + partition. + + Example: + + tmpfs /dev/shm tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /dev/shm + with the configured options: + # mount -o remount /dev/shm + + + Note: + It is recommended to use tmpfs + as the device/filesystem type as /dev/shm + is used as shared memory space by applications. + + + + + + + + + + + + + + Configure /home + + +Please note that home directories can be mounted anywhere and are not necessarily restricted to /home +, nor restricted to a single location, nor is the name restricted in any way. + +Finding user home directories can be done by looking in /etc/passwd +, looking over the mounted file systems with mount + or querying the relevant database with getent +. + for user in $(awk -F ':' '{print $1}' /etc/passwd); do echo "${user} - $(sudo getent passwd ${user} | awk -F ':' '{print $NF}')"; done + + + + Ensure separate partition exists for /home + + +The /home + directory is used to support disk storage needs of local users. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +When modifying /home + it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multi-user mode. + + + + +The default installation only creates a single / + partition. Since the /home + directory contains user generated data, there is a risk of resource exhaustion. It will essentially have the whole disk available to fill up and impact the system as a whole. In addition, other operations on the system could fill up the disk unrelated to /home + and impact all local users. + +Configuring /home + as its own file system allows an administrator to set additional mount options such as noexec/nosuid/nodev +. These options limit an attacker's ability to create exploits on the system. In the case of /home + options such as usrquota/grpquota + may be considered to limit the impact that users can have on each other with regards to disk resource exhaustion. Other options allow for specific behavior. See man mount + for exact details regarding filesystem-independent and filesystem-specific options. + +As /home + contains user data, care should be taken to ensure the security and integrity of the data and mount point. + + + + AJ Lewis, "LVM HOWTO", http://tldp.org/HOWTO/LVM-HOWTO/ + NIST SP 800-53 Rev. 5: CM-7 + + + + +For new installations, during installation create a custom partition setup and specify a separate partition for /home +. + +For systems that were previously installed, create a new partition and configure /etc/fstab + as appropriate. + Impact: + + Resizing filesystems is a common activity in cloud-hosted servers. Separate filesystem partitions may prevent successful resizing or may require the installation of additional tools solely for the purpose of resizing operations. The use of these additional tools may introduce their own security considerations. + + + + + + + + + + + + + + Ensure nodev option set on /home partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /home + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /home +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /home +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /home + partition. + + Example: + + <device> /home <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /home + with the configured options: + # mount -o remount /home + + + + + + + + + + + + + + Ensure nosuid option set on /home partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /home + filesystem is only intended for user file storage, set this option to ensure that users cannot create setuid + files in /home +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /home +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /home + partition. + + Example: + + <device> /home <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /home + with the configured options: + # mount -o remount /home + + + + + + + + + + + + + + + Configure /var + + +The /var + directory is used by daemons and other system services to temporarily store dynamic data. Some directories created by these processes may be world-writable. + + + Ensure separate partition exists for /var + + +The /var + directory is used by daemons and other system services to temporarily store dynamic data. Some directories created by these processes may be world-writable. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +When modifying /var + it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multi-user mode. + + + + +The reasoning for mounting /var + on a separate partition is as follows. + +The default installation only creates a single / + partition. Since the /var + directory may contain world-writable files and directories, there is a risk of resource exhaustion. It will essentially have the whole disk available to fill up and impact the system. In addition, other operations on the system could fill up the disk unrelated to /var + and cause unintended behavior across the system as the disk is full. See man auditd.conf + for details. + +Configuring /var + as its own file system allows an administrator to set additional mount options such as noexec/nosuid/nodev +. These options limit an attacker's ability to create exploits on the system. Other options allow for specific behavior. See man mount + for exact details regarding filesystem-independent and filesystem-specific options. + +An example of exploiting /var + may be an attacker establishing a hard-link to a system setuid + program and waiting for it to be updated. Once the program is updated, the hard-link can be broken and the attacker would have their own copy of the program. If the program happened to have a security vulnerability, the attacker could continue to exploit the known flaw. + + + + AJ Lewis, "LVM HOWTO", http://tldp.org/HOWTO/LVM-HOWTO/ + NIST SP 800-53 Rev. 5: CM-7 + + + + +For new installations, during installation create a custom partition setup and specify a separate partition for /var +. + +For systems that were previously installed, create a new partition and configure /etc/fstab + as appropriate. + Impact: + + Resizing filesystems is a common activity in cloud-hosted servers. Separate filesystem partitions may prevent successful resizing or may require the installation of additional tools solely for the purpose of resizing operations. The use of these additional tools may introduce their own security considerations. + + + + + + + + + + + + + + Ensure nodev option set on /var partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /var +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /var + partition. + + Example: + + <device> /var <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var + with the configured options: + # mount -o remount /var + + + + + + + + + + + + + + Ensure nosuid option set on /var partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var + filesystem is only intended for variable files such as logs, set this option to ensure that users cannot create setuid + files in /var +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /var + partition. + + Example: + + <device> /var <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var + with the configured options: + # mount -o remount /var + + + + + + + + + + + + + + + Configure /var/tmp + + +The /var/tmp + directory is a world-writable directory used for temporary storage by all users and some applications. Temporary files residing in /var/tmp + are to be preserved between reboots. + + + Ensure separate partition exists for /var/tmp + + +The /var/tmp + directory is a world-writable directory used for temporary storage by all users and some applications. Temporary files residing in /var/tmp + are to be preserved between reboots. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +When modifying /var/tmp + it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multi-user mode. + + + + +The default installation only creates a single / + partition. Since the /var/tmp + directory is world-writable, there is a risk of resource exhaustion. In addition, other operations on the system could fill up the disk unrelated to /var/tmp + and cause potential disruption to daemons as the disk is full. + +Configuring /var/tmp + as its own file system allows an administrator to set additional mount options such as noexec/nosuid/nodev +. These options limit an attacker's ability to create exploits on the system. + + + + AJ Lewis, "LVM HOWTO", http://tldp.org/HOWTO/LVM-HOWTO/ + NIST SP 800-53 Rev. 5: CM-7 + + + + +For new installations, during installation create a custom partition setup and specify a separate partition for /var/tmp +. + +For systems that were previously installed, create a new partition and configure /etc/fstab + as appropriate. + Impact: + + Resizing filesystems is a common activity in cloud-hosted servers. Separate filesystem partitions may prevent successful resizing or may require the installation of additional tools solely for the purpose of resizing operations. The use of these additional tools may introduce their own security considerations. + + + + + + + + + + + + + + Ensure nodev option set on /var/tmp partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/tmp + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /var/tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/tmp +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /var/tmp + partition. + + Example: + + <device> /var/tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/tmp + with the configured options: + # mount -o remount /var/tmp + + + + + + + + + + + + + + Ensure nosuid option set on /var/tmp partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/tmp + filesystem is only intended for temporary file storage, set this option to ensure that users cannot create setuid + files in /var/tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/tmp +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /var/tmp + partition. + + Example: + + <device> /var/tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/tmp + with the configured options: + # mount -o remount /var/tmp + + + + + + + + + + + + + + Ensure noexec option set on /var/tmp partition + + +The noexec + mount option specifies that the filesystem cannot contain executable binaries. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/tmp + filesystem is only intended for temporary file storage, set this option to ensure that users cannot run executable binaries from /var/tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/tmp +. + +Edit the /etc/fstab + file and add noexec + to the fourth field (mounting options) for the /var/tmp + partition. + + Example: + + <device> /var/tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/tmp + with the configured options: + # mount -o remount /var/tmp + + + + + + + + + + + + + + + Configure /var/log + + +The /var/log + directory is used by system services to store log data. + + + Ensure separate partition exists for /var/log + + +The /var/log + directory is used by system services to store log data. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +When modifying /var/log + it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multiuser mode. + + + + +The default installation only creates a single / + partition. Since the /var/log + directory contains log files which can grow quite large, there is a risk of resource exhaustion. It will essentially have the whole disk available to fill up and impact the system as a whole. + +Configuring /var/log + as its own file system allows an administrator to set additional mount options such as noexec/nosuid/nodev +. These options limit an attackers ability to create exploits on the system. Other options allow for specific behavior. See man mount + for exact details regarding filesystem-independent and filesystem-specific options. + +As /var/log + contains log files, care should be taken to ensure the security and integrity of the data and mount point. + + + + AJ Lewis, "LVM HOWTO", http://tldp.org/HOWTO/LVM-HOWTO/ + NIST SP 800-53 Rev. 5: CM-7 + + + + +For new installations, during installation create a custom partition setup and specify a separate partition for /var/log + . + +For systems that were previously installed, create a new partition and configure /etc/fstab + as appropriate. + Impact: + + Resizing filesystems is a common activity in cloud-hosted servers. Separate filesystem partitions may prevent successful resizing, or may require the installation of additional tools solely for the purpose of resizing operations. The use of these additional tools may introduce their own security considerations. + + + + + + + + + + + + + + Ensure nodev option set on /var/log partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /var/log +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /var/log + partition. + + Example: + + <device> /var/log <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log + with the configured options: + # mount -o remount /var/log + + + + + + + + + + + + + + Ensure nosuid option set on /var/log partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log + filesystem is only intended for log files, set this option to ensure that users cannot create setuid + files in /var/log +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /var/log + partition. + + Example: + + <device> /var/log <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log + with the configured options: + # mount -o remount /var/log + + + + + + + + + + + + + + Ensure noexec option set on /var/log partition + + +The noexec + mount option specifies that the filesystem cannot contain executable binaries. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log + filesystem is only intended for log files, set this option to ensure that users cannot run executable binaries from /var/log +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log +. + +Edit the /etc/fstab + file and add noexec + to the fourth field (mounting options) for the /var/log + partition. + + Example: + + <device> /var/log <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log + with the configured options: + # mount -o remount /var/log + + + + + + + + + + + + + + + Configure /var/log/audit + + +The auditing daemon, auditd +, stores log data in the /var/log/audit + directory. + + + Ensure separate partition exists for /var/log/audit + + +The auditing daemon, auditd +, stores log data in the /var/log/audit + directory. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +When modifying /var/log/audit + it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multi-user mode. + + + + +The default installation only creates a single / + partition. Since the /var/log/audit + directory contains the audit.log + file which can grow quite large, there is a risk of resource exhaustion. It will essentially have the whole disk available to fill up and impact the system as a whole. In addition, other operations on the system could fill up the disk unrelated to /var/log/audit + and cause auditd + to trigger its space_left_action + as the disk is full. See man auditd.conf + for details. + +Configuring /var/log/audit + as its own file system allows an administrator to set additional mount options such as noexec/nosuid/nodev +. These options limit an attacker's ability to create exploits on the system. Other options allow for specific behavior. See man mount + for exact details regarding filesystem-independent and filesystem-specific options. + +As /var/log/audit + contains audit logs, care should be taken to ensure the security and integrity of the data and mount point. + + + + AJ Lewis, "LVM HOWTO", http://tldp.org/HOWTO/LVM-HOWTO/ + NIST SP 800-53 Rev. 5: CM-7 + + + + +For new installations, during installation create a custom partition setup and specify a separate partition for /var/log/audit +. + +For systems that were previously installed, create a new partition and configure /etc/fstab + as appropriate. + Impact: + + Resizing filesystems is a common activity in cloud-hosted servers. Separate filesystem partitions may prevent successful resizing or may require the installation of additional tools solely for the purpose of resizing operations. The use of these additional tools may introduce their own security considerations. + + + + + + + + + + + + + + Ensure nodev option set on /var/log/audit partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log/audit + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /var/log/audit +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log/audit +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /var/log/audit + partition. + + Example: + + <device> /var/log/audit <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log/audit + with the configured options: + # mount -o remount /var/log/audit + + + + + + + + + + + + + + Ensure nosuid option set on /var/log/audit partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log/audit + filesystem is only intended for variable files such as logs, set this option to ensure that users cannot create setuid + files in /var/log/audit +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log/audit +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /var/log/audit + partition. + + Example: + + <device> /var/log/audit <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log/audit + with the configured options: + # mount -o remount /var/log/audit + + + + + + + + + + + + + + Ensure noexec option set on /var/log/audit partition + + +The noexec + mount option specifies that the filesystem cannot contain executable binaries. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log/audit + filesystem is only intended for audit logs, set this option to ensure that users cannot run executable binaries from /var/log/audit +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log/audit +. + +Edit the /etc/fstab + file and add noexec + to the fourth field (mounting options) for the /var/log/audit + partition. + + Example: + + <device> /var/log/audit <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log/audit + with the configured options: + # mount -o remount /var/log/audit + + + + + + + + + + + + + + +
+ + Package Management + + Patch management procedures may vary widely between enterprises. Large enterprises may choose to install a local updates server that can be used in place of their distributions servers, whereas a single deployment of a system may prefer to get updates directly. Updates can be performed automatically or manually, depending on the site's policy for patch management. Organizations may prefer to test patches against their environment on a non-production system before rolling out to production. + Outdated software is vulnerable to cyber criminals and hackers. Software updates help reduce the risk to your organization. The release of software update notes often reveal the patched exploitable entry points to the public. Public knowledge of these exploits cans your organization more vulnerable to malicious actors attempting to gain entry to your system's data. + Software updates often offer new and improved features and speed enhancements + For the purpose of this benchmark, the requirement is to ensure that a patch management process is defined and maintained, the specifics of which are left to the organization. + + + Configure Package Repositories + + Patch management procedures may vary widely between enterprises. Large enterprises may choose to install a local updates server that can be used in place of their distributions servers, whereas a single deployment of a system may prefer to get updates directly. Updates can be performed automatically or manually, depending on the site's policy for patch management. Organizations may prefer to test patches against their environment on a non-production system before rolling out to production. + Outdated software is vulnerable to cyber criminals and hackers. Software updates help reduce the risk to your organization. The release of software update notes often reveals the patched exploitable entry points to the public. Public knowledge of these exploits can leave your organization more vulnerable to malicious actors attempting to gain access to your system's data. + + Note: + Creation of an appropriate patch management policy is left to the organization. + + + Ensure GPG keys are configured + + The RPM Package Manager implements GPG key signing to verify package integrity during and after installation. + + + + + + + Applications + Protect + + + + Applications + Protect + + + + + + Applications + Protect + + + + Applications + Protect + + + + + +Fedora public keys: https://getfedora.org/security/ + + + + + It is important to ensure that updates are obtained from a valid source to protect against spoofing that could lead to the inadvertent installation of malware on the system. To this end, verify that GPG keys are configured correctly for your system. + + + + + + NIST SP 800-53 Rev. 5: SI-2 + + + + Update your package manager GPG keys in accordance with site policy. + + + + + + Ensure gpgcheck is globally activated + + +The gpgcheck + option, found in the main section of the /etc/dnf/dnf.conf + and individual /etc/yum.repos.d/* + files, determines if an RPM package's signature is checked prior to its installation. + + + + + + + Applications + Protect + + + + + + Applications + Protect + + + + + + It is important to ensure that an RPM's package signature is always checked prior to installation to ensure that the software is obtained from a trusted source. + + + + NIST SP 800-53 Rev. 5: SI-2 + + + + +Edit /etc/dnf/dnf.conf + and set gpgcheck=1 +: + + Example + + # sed -i 's/^gpgcheck\s*=\s*.*/gpgcheck=1/' /etc/dnf/dnf.conf + + +Edit any failing files in /etc/yum.repos.d/* + and set all instances starting with gpgcheck + to 1 +. + + Example: + + # find /etc/yum.repos.d/ -name "*.repo" -exec echo "Checking:" {} \; -exec sed -i 's/^gpgcheck\s*=\s*.*/gpgcheck=1/' {} \; + + + + + + + + + + + + + + + + + + + + Ensure repo_gpgcheck is globally activated + + +The repo_gpgcheck + option, found in the main section of the /etc/dnf/dnf.conf + and individual /etc/yum.repos.d/* + files, will perform a GPG signature check on the repodata. + + + + + + + Applications + Protect + + + + + + Applications + Protect + + + + + + It is important to ensure that the repository data signature is always checked prior to installation to ensure that the software is not tampered with in any way. + + + + NIST SP 800-53 Rev. 5: SI-2 + + + + + Global configuration + + +Edit /etc/dnf/dnf.conf + and set repo_gpgcheck=1 + in the [main] + section. + + Example: + + +[main]
+repo_gpgcheck=1 +
+ + Per repository configuration + + First check that the particular repository support GPG checking on the repodata. + +Edit any failing files in /etc/yum.repos.d/* + and set all instances starting with repo_gpgcheck + to 1 +. + Impact: + + +Not all repositories, notably RedHat, support repo_gpgcheck +. Take care to set this value to false (default) for particular repositories that do not support it. If enabled on repositories that do not support repo_gpgcheck + installation of packages will fail. + +Research is required by the user to determine which repositories is configured on the local system and, from that list, which support repo_gpgcheck +. + +
+
+
+
+ + Ensure package manager repositories are configured + + Systems need to have the respective package manager repositories configured to ensure that the system is able to receive the latest patches and updates. + + + + + + + Applications + Protect + + + + Applications + Protect + + + + + + Applications + Protect + + + + Applications + Protect + + + + + +For further information about Fedora repositories see: https://docs.fedoraproject.org/en-US/quick-docs/repositories/ + + + + + If a system's package repositories are misconfigured, important patches may not be identified or a rogue repository could introduce compromised software. + + + + + + NIST SP 800-53 Rev. 5: SI-2 + + + + Configure your package manager repositories according to site policy. + + + + +
+ + Configure Package Updates + + + Ensure updates, patches, and additional security software are installed + + Periodically patches are released for included software either due to security flaws or to include additional functionality. + + + + + + + Applications + Protect + + + + Applications + Protect + + + + + + Applications + Protect + + + + Applications + Protect + + + + + Site policy may mandate a testing period before install onto production systems for available updates. + # dnf check-update + + + + + Newer patches may contain security enhancements that would not be available through the latest full update. As a result, it is recommended that the latest software patches be used to take advantage of the latest functionality. As with any software installation, organizations need to determine if a given update meets their requirements and verify the compatibility and supportability of any additional software against the update revision that is selected. + + + + + + NIST SP 800-53 Rev. 5: SI-2 + + + + Use your package manager to update all packages on the system according to site policy. + The following command will install all available updates: + # dnf update + + Once the update process is complete, verify if reboot is required to load changes. + dnf needs-restarting -r + + + + + + +
+ + Mandatory Access Control + + Mandatory Access Control (MAC) provides an additional layer of access restrictions to processes on top of the base Discretionary Access Controls. By restricting how processes can access files and resources on a system the potential impact from vulnerabilities in the processes can be reduced. + + Impact: + Mandatory Access Control limits the capabilities of applications and daemons on a system, while this can prevent unauthorized access the configuration of MAC can be complex and difficult to implement correctly preventing legitimate access from occurring. + + + Configure SELinux + + SELinux implements Mandatory Access Control (MAC). Every process and system resource has a special security label called an SELinux context. A SELinux context, sometimes referred to as an SELinux label, is an identifier which abstracts away the system-level details and focuses on the security properties of the entity. Not only does this provide a consistent way of referencing objects in the SELinux policy, but it also removes any ambiguity that can be found in other identification methods. For example, a file can have multiple valid path names on a system that makes use of bind mounts. + The SELinux policy uses these contexts in a series of rules which define how processes can interact with each other and the various system resources. By default, the policy does not allow any interaction unless a rule explicitly grants access. + In Fedora 28 Family Linux distributions, system services are controlled by the systemd daemon; systemd starts and stops all services, and users and processes communicate with systemd using the systemctl utility. The systemd daemon can consult the SELinux policy and check the label of the calling process and the label of the unit file that the caller tries to manage, and then ask SELinux whether or not the caller is allowed the access. This approach strengthens access control to critical system capabilities, which include starting and stopping system services. + This automatically limits the damage that the software can do to files accessible by the calling user. The user does not need to take any action to gain this benefit. For an action to occur, both the traditional DAC permissions must be satisfied as well as the SELinux MAC rules. The action will not be allowed if either one of these models does not permit the action. In this way, SELinux rules can only make a system's permissions more restrictive and secure. SELinux requires a complex policy to allow all the actions required of a system under normal operation. + Two such policies have been designed for use with Fedora 28 Family Linux distributions and are included with the system: + + + targeted + - Targeted processes run in their own domain, called a confined domain. In a confined domain, the files that a targeted process has access to are limited. If a confined process is compromised by an attacker, the attacker’s access to resources and the possible damage they can do is also limited. SELinux denies access to these resources and logs the denial. + + mls + - Implements Multi-Level Security (MLS), which introduces even more kinds of labels (sensitivity and category) and rules that govern access based on these. + + +This section provides guidance for the configuration of the targeted + policy. + + Note: + + + Remember that SELinux policy rules are checked after DAC rules. SELinux policy rules are not used if DAC rules deny access first, which means that no SELinux denial is logged if the traditional DAC rules prevent the access. + This section only applies if SELinux is in use on the system. Additional Mandatory Access Control systems exist. + To avoid incorrect SELinux labeling and subsequent problems, ensure that you start services using a systemctl start command. + + + References: + + + +NSA SELinux resources: + + + https://www.nsa.gov/Research/Technical-Papers-Brochures/smdsearch14229/selinux + + + + +Fedora SELinux resources: + + +Getting started with SELinux: https://docs.fedoraproject.org/en-US/quick-docs/getting-started-with-selinux + + +User Guide: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/using_selinux/index + + + + +SELinux Project web page and wiki: + + + http://www.selinuxproject.org + + + + + + + Ensure SELinux is installed + + SELinux provides Mandatory Access Control. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Without a Mandatory Access Control system installed only the default Discretionary Access Control system will be available. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following command to install SELinux +: + # dnf install libselinux + + + + + + + + + + + + Ensure SELinux is not disabled in bootloader configuration + + Configure SELINUX to be enabled at boot time and verify that it has not been overwritten by the grub boot parameters. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + This recommendation is designed around the grub 2 bootloader, if another bootloader is in use in your environment enact equivalent settings. + + grubby + is a command line tool for updating and displaying information about the configuration files for the grub2 and zipl boot loaders. It is primarily designed to be used from scripts which install new kernels and need to find information about the current boot environment. + + +All bootloaders define the boot entries as individual configuration fragments that are stored by default in /boot/loader/entries +. The format for the config files is specified at https://systemd.io/BOOT_LOADER_SPECIFICATION +. The grubby tool is used to update and display the configuration defined in the BootLoaderSpec + fragment files. + There are a number of ways to specify the kernel used for --info, --remove-kernel, and --update-kernel. Specifying DEFAULT or ALL selects the de‐fault entry and all of the entries, respectively. Also, the title of a boot entry may be specified by using TITLE=title as the argument; all entries with that title are used. + + + + + SELinux must be enabled at boot time in your grub configuration to ensure that the controls it provides are not overridden. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following command to remove the selinux=0 + and enforcing=0 + parameters: + grubby --update-kernel ALL --remove-args "selinux=0 enforcing=0" + + +Run the following command to remove the selinux=0 + and enforcing=0 + parameters if they were created by the deprecated grub2-mkconfig + command: + # grep -Prsq -- '\h*([^#\n\r]+\h+)?kernelopts=([^#\n\r]+\h+)?(selinux|enforcing)=0\b' /boot/grub2 /boot/efi && grub2-mkconfig -o "$(grep -Prl -- '\h*([^#\n\r]+\h+)?kernelopts=([^#\n\r]+\h+)?(selinux|enforcing)=0\b' /boot/grub2 /boot/efi)" + + Impact: + + Files created while SELinux is disabled are not labeled at all. This behavior causes problems when changing to enforcing mode because files are labeled incorrectly or are not labeled at all. To prevent incorrectly labeled and unlabeled files from causing problems, file systems are automatically relabeled when changing from the disabled state to permissive or enforcing mode. This can be a long running process that should be accounted for as it may extend downtime during initial re-boot. + + + + + + + + + + + + + + + + + + + Ensure SELinux policy is configured + + Configure SELinux to meet or exceed the default targeted policy, which constrains daemons and system software only. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +If your organization requires stricter policies, ensure that they are set in the /etc/selinux/config + file. + + + + Security configuration requirements vary from site to site. Some sites may mandate a policy that is stricter than the default policy, which is perfectly acceptable. This item is intended to ensure that at least the default recommendations are met. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Edit the /etc/selinux/config + file to set the SELINUXTYPE parameter: + SELINUXTYPE=targeted + + + + + + + + + + + + + + + + + Ensure the SELinux mode is not disabled + + SELinux can run in one of three modes: disabled, permissive, or enforcing: + + + Enforcing + - Is the default, and recommended, mode of operation; in enforcing mode SELinux operates normally, enforcing the loaded security policy on the entire system. + + Permissive + - The system acts as if SELinux is enforcing the loaded security policy, including labeling objects and emitting access denial entries in the logs, but it does not actually deny any operations. While not recommended for production systems, permissive mode can be helpful for SELinux policy development. + + Disabled + - Is strongly discouraged; not only does the system avoid enforcing the SELinux policy, it also avoids labeling any persistent objects such as files, making it difficult to enable SELinux in the future + + + Note: + You can set individual domains to permissive mode while the system runs in enforcing mode. For example, to make the httpd_t domain permissive: + # semanage permissive -a httpd_t + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Running SELinux in disabled mode is strongly discouraged; not only does the system avoid enforcing the SELinux policy, it also avoids labeling any persistent objects such as files, making it difficult to enable SELinux in the future. + + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/selinux_users_and_administrators_guide/sect-security-enhanced_linux-introduction-selinux_modes + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + Run one of the following commands to set SELinux's running mode: + +To set SELinux mode to Enforcing +: + # setenforce 1 + + + - OR - + + +To set SELinux mode to Permissive +: + # setenforce 0 + + +Edit the /etc/selinux/config + file to set the SELINUX parameter: + For Enforcing mode: + SELINUX=enforcing + + + - OR - + + For Permissive mode: + SELINUX=permissive + + + + + + + + + + + + + + + + + + + + + + + + Ensure the SELinux mode is enforcing + + SELinux can run in one of three modes: disabled, permissive, or enforcing: + + + Enforcing + - Is the default, and recommended, mode of operation; in enforcing mode SELinux operates normally, enforcing the loaded security policy on the entire system. + + Permissive + - The system acts as if SELinux is enforcing the loaded security policy, including labeling objects and emitting access denial entries in the logs, but it does not actually deny any operations. While not recommended for production systems, permissive mode can be helpful for SELinux policy development. + + Disabled + - Is strongly discouraged; not only does the system avoid enforcing the SELinux policy, it also avoids labeling any persistent objects such as files, making it difficult to enable SELinux in the future + + + Note: + You can set individual domains to permissive mode while the system runs in enforcing mode. For example, to make the httpd_t domain permissive: + # semanage permissive -a httpd_t + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Running SELinux in disabled mode the system not only avoids enforcing the SELinux policy, it also avoids labeling any persistent objects such as files, making it difficult to enable SELinux in the future. + Running SELinux in Permissive mode, though helpful for developing SELinux policy, only logs access denial entries, but does not deny any operations. + + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/selinux_users_and_administrators_guide/sect-security-enhanced_linux-introduction-selinux_modes + CCI-002165: The information system enforces organization-defined discretionary access control policies over defined subjects and objects. + NIST SP 800-53 Revision 4 :: AC-3 (4) + CCI-002696: The information system verifies correct operation of organization-defined security functions. + NIST SP 800-53 Revision 4 :: SI-6 a + + + + Run the following command to set SELinux's running mode: + # setenforce 1 + + +Edit the /etc/selinux/config + file to set the SELINUX parameter: + For Enforcing mode: + SELINUX=enforcing + + Impact: + + Running SELinux in Enforcing mode may block intended access to files or processes if the SELinux policy is not correctly configured. If this occurs, review the system logs for details and update labels or policy as appropriate. + + + + + + + + + + + + + + + + + + + + + + + + Ensure no unconfined services exist + + Unconfined processes run in unconfined domains + + + + + + + Data + Protect + + + + + + Devices + Protect + + + + + Occasionally certain daemons such as backup or centralized management software may require running unconfined. Any such software should be carefully analyzed and documented before such an exception is made. + + + + For unconfined processes, SELinux policy rules are applied, but policy rules exist that allow processes running in unconfined domains almost all access. Processes running in unconfined domains fall back to using DAC rules exclusively. If an unconfined process is compromised, SELinux does not prevent an attacker from gaining access to system resources and data, but of course, DAC rules are still used. SELinux is a security enhancement on top of DAC rules – it does not replace them + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + https://nb.fedorapeople.org/cvsfedora/web/html/docs/selinux-guide/f10/en-US/sect-Security-Enhanced_Linux-Targeted_Policy-Unconfined_Processes.html + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/using_selinux/managing-confined-and-unconfined-users_using-selinux + https://access.redhat.com/documentation/id-id/red_hat_enterprise_linux/9/pdf/using_selinux/red_hat_enterprise_linux-9-using_selinux-en-us.pdf + + + + Investigate any unconfined processes found during the audit action. If necessary create a customize SELinux policy to allow necessary actions for the service. + + Warning: + Knowledge about creating and configuring SELinux policies is needed. A Basic example on how to create a policy is included below. + + + Identify the unconfined service: determine the name and process of the service + + + Identify the functionality: determine if the functionality is required for operations + + + Create or add to the custom allow list in the SELinux policy configuration + + + + Example SELinux policy configuration: service_allowlist_policy.te + + +# Example SELinux policy configuration for allowing access to specific actions and resources for a service
+
+module my_service 1.0;
+
+require {
+ type my_service_t;
+ type system_resource_t;
+ class file { read write execute };
+ class dir { read write add_name };
+ class tcp_socket name_connect;
+}
+
+allow my_service_t system_resource_t:file { read write execute }; # Allow my_service_t to read, write, and execute files with the system_resource_t context
+
+allow my_service_t system_resource_t:dir { read write add_name }; # Allow my_service_t to read and write to directories with the system_resource_t context
+
+allow my_service_t system_resource_t:tcp_socket name_connect; # Allow my_service_t to establish TCP connections +
+ + Compile the policy + + # checkmodule -M -, -o service_allowlist_policy.mod service_allowlist_policy.te + + + Create the package + + # semodule_package -o service_allowlist_policy.pp -m service_allowlist_policy.mod + + + Load the policy + + # semodule -i service_allowlist_policy.pp + + + Apply the policy to the service + + # chcon -t se service_allowlist_policy /path/to/service_binary + + Impact: + + Confining a service that inherently requires unconfined access to function may disrupt its intended operations. This restriction may lead to downtime, degraded performance, or loss in functionality. It is crucial to analyze and adjust SELinux policies in accordance with site security policies and operational requirements. + +
+
+
+ + + + + + +
+ + Ensure the MCS Translation Service (mcstrans) is not installed + + +The mcstransd + daemon provides category label information to client processes requesting information. The label translations are defined in /etc/selinux/targeted/setrans.conf + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Since this service is not used very often, remove it to reduce the amount of potentially vulnerable code running on the system. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following command to uninstall mcstrans +: + # dnf remove mcstrans + + + + + + + + + + + + Ensure SETroubleshoot is not installed + + The SETroubleshoot service notifies desktop users of SELinux denials through a user-friendly interface. The service provides important information around configuration errors, unauthorized intrusions, and other potential errors. + + + + + + + Devices + Protect + + + + + + Data + Protect + + + + + + The SETroubleshoot service is an unnecessary daemon to have running on a server, especially if X Windows is disabled. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following command to uninstall setroubleshoot +: + # dnf remove setroubleshoot + + + + + + + + + + +
+
+ + Configure Bootloader + + The recommendations in this section focus on securing the bootloader and settings involved in the boot process directly. + + Notes: + + + +In Fedora 28 based distributions, the kernel command-line parameters for systems using the GRUB2 bootloader were defined in the kernelopts + environment variable. This variable was stored in the /boot/grub2/grubenv + file for each kernel boot entry. However, storing the kernel command-line parameters using kernelopts + was not robust. Therefore, the kernelopts + has been removed and the kernel command-line parameters are now stored in the Boot Loader Specification (BLS) snippet, instead of in the /boot/loader/entries/<KERNEL_BOOT_ENTRY>.conf + file. + +Boot loader configuration files are unified across CPU architectures + + +Configuration files for the GRUB boot loader are now stored in the /boot/grub2/ + directory on all supported CPU architectures. The /boot/efi/EFI/redhat/grub.cfg + file, which GRUB previously used as the main configuration file on UEFI systems, now simply loads the /boot/grub2/grub.cfg + file. + +This change simplifies the layout of the GRUB configuration file, improves user experience, and provides the following notable benefits: + + You can boot the same installation with either EFI or legacy BIOS. + You can use the same documentation and commands for all architectures. + GRUB configuration tools are more robust, because they no longer rely on symbolic links and they do not have to handle platform-specific cases. + The usage of the GRUB configuration files is aligned with images generated by CoreOS Assembler (COSA) and OSBuild. + The usage of the GRUB configuration files is aligned with other Linux distributions. + Fedora 28 based distributions no longer boot on 32-bit UEFI + + + + + +Support for the 32-bit UEFI firmware was removed from the GRUB and shim boot loaders. As a consequence, Fedora 28 based distributions require a 64-bit UEFI, and can no longer boot on 64-bit systems that use a 32-bit UEFI. + + The following packages have been removed as part of this change: + + grub2-efi-ia32 + + + grub2-efi-ia32-cdboot + + + grub2-efi-ia32-modules + + + shim-ia32 + + + + + + Reference: + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/considerations_in_adopting_rhel_8/index#kernel_considerations-in-adopting-RHEL-8 + + + + Ensure bootloader password is set + + Setting the boot loader password will require that anyone rebooting the system must enter a password before being able to set command line boot parameters. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + This recommendation is designed around the grub2 bootloader, if LILO or another bootloader is in use in your environment enact equivalent settings. + + grub2-setpassword + outputs the user.cfg + file which contains the hashed GRUB bootloader password. This utility only supports configurations where there is a single root user. + + + + Requiring a boot password upon execution of the boot loader will prevent an unauthorized user from entering boot parameters or changing the boot partition. This prevents users from weakening security (e.g. turning off SELinux at boot time). + + + + NIST SP 800-53 Rev. 5: AC-3 + + + + +Create an encrypted password with grub2-setpassword +: + +# grub2-setpassword
+
+Enter password: <password>
+Confirm password: <password> +
+ Impact: + + +If password protection is enabled, only the designated superuser can edit a GRUB 2 menu item by pressing e + or access the GRUB 2 command line by pressing c + + If GRUB 2 is set up to boot automatically to a password-protected menu entry the user has no option to back out of the password prompt to select another menu entry. Holding the SHIFT key will not display the menu in this case. The user must enter the correct username and password. If unable, the configuration files will have to be edited via the LiveCD or other means to fix the problem + +
+
+
+ + + + + + + + + +
+ + Ensure access to bootloader config is configured + + The grub files contain information on boot settings and passwords for unlocking boot options. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + This recommendation is designed around the grub bootloader, if LILO or another bootloader is in use in your environment enact equivalent settings. + + + + Setting the permissions to read and write for root only prevents non-root users from seeing the boot parameters or changing them. Non-root users who read the boot parameters may be able to identify weaknesses in security upon boot and be able to exploit them. + + + + NIST SP 800-53 Rev. 5: AC-3 + + + + Run the following to update the mode, ownership, and group ownership of the grub configuration files: + + - IF - + the system uses UEFI (Files located in /boot/efi/EFI/* +) + +Edit /etc/fstab + and add the fmask=0077 +, uid=0 +, and gid=0 + options: + + Example: + + <device> /boot/efi vfat defaults,umask=0027,fmask=0077,uid=0,gid=0 0 0 + + + Note: + This may require a re-boot to enable the change + + - OR - + + + - IF - + the system uses BIOS (Files located in /boot/grub2/* +) + Run the following commands to set ownership and permissions on your grub configuration file(s): + +# [ -f /boot/grub2/grub.cfg ] && chown root:root /boot/grub2/grub.cfg
+# [ -f /boot/grub2/grub.cfg ] && chmod u-x,go-rwx /boot/grub2/grub.cfg
+
+# [ -f /boot/grub2/grubenv ] && chown root:root /boot/grub2/grubenv
+# [ -f /boot/grub2/grubenv ] && chmod u-x,go-rwx /boot/grub2/grubenv
+
+# [ -f /boot/grub2/user.cfg ] && chown root:root /boot/grub2/user.cfg
+# [ -f /boot/grub2/user.cfg ] && chmod u-x,go-rwx /boot/grub2/user.cfg +
+
+
+
+ + + + + + +
+
+ + Configure Additional Process Hardening + + + Ensure address space layout randomization is enabled + + Address space layout randomization (ASLR) is an exploit mitigation technique which randomly arranges the address space of key data areas of a process. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Randomly placing virtual memory regions will make it difficult to write memory page exploits as the memory placement will be consistently shifting. + + + + CCI-000366: The organization implements the security configuration settings + NIST SP 800-53 Rev. 5: CM-6 + NIST SP 800-53A :: CM-6.1 (iv) + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + kernel.randomize_va_space = 2 + + + + Example: + + +# printf "
+kernel.randomize_va_space = 2
+" >> /etc/sysctl.d/60-kernel_sysctl.conf +
+ Run the following command to set the active kernel parameter: + # sysctl -w kernel.randomize_va_space=2 + + + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + +
+ + Ensure ptrace_scope is restricted + + +The ptrace() + system call provides a means by which one process (the "tracer") may observe and control the execution of another process (the "tracee"), and examine and change the tracee's memory and registers. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +Ptrace is very rarely used by regular applications and is mostly used by debuggers such as gdb + and strace +. + + + + If one application is compromised, it would be possible for an attacker to attach to other running processes (e.g. Bash, Firefox, SSH sessions, GPG agent, etc) to extract additional credentials and continue to expand the scope of their attack. + Enabling restricted mode will limit the ability of a compromised process to PTRACE_ATTACH on other processes running under the same user. With restricted mode, ptrace will continue to work with root user. + + + + https://www.kernel.org/doc/Documentation/security/Yama.txt + https://github.com/raj3shp/termspy + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + kernel.yama.ptrace_scope = 1 + + + + Example: + + +# printf "
+kernel.yama.ptrace_scope = 1
+" >> /etc/sysctl.d/60-kernel_sysctl.conf +
+ Run the following command to set the active kernel parameter: + # sysctl -w kernel.yama.ptrace_scope=1 + + + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + +
+ + Ensure core dump backtraces are disabled + + A core dump is the memory of an executable program. It is generally used to determine why a program aborted. It can also be used to glean confidential information from a core file. + + + A core dump includes a memory image taken at the time the operating system terminates an application. The memory image could contain sensitive data and is generally useful only for developers trying to debug problems, increasing the risk to the system. + + https://www.freedesktop.org/software/systemd/man/coredump.conf.html + NIST SP 800-53 Rev. 5: CM-6b + + + + +Create or edit the file /etc/systemd/coredump.conf +, or a file in the /etc/systemd/coredump.conf.d + directory ending in .conf +. + +Edit or add the following line in the [Coredump] + section: + ProcessSizeMax=0 + + + Example: + + +#!/usr/bin/env bash
+
+{
+ [ ! -d /etc/systemd/coredump.conf.d/ ] && mkdir /etc/systemd/coredump.conf.d/
+ if grep -Psq -- '^\h*\[Coredump\]' /etc/systemd/coredump.conf.d/60-coredump.conf; then
+ printf '%s\n' "ProcessSizeMax=0" >> /etc/systemd/coredump.conf.d/60-coredump.conf
+ else
+ printf '%s\n' "[Coredump]" "ProcessSizeMax=0" >> /etc/systemd/coredump.conf.d/60-coredump.conf
+ fi
+} +
+
+
+
+ + + + + + + +
+ + Ensure core dump storage is disabled + + A core dump is the memory of an executable program. It is generally used to determine why a program aborted. It can also be used to glean confidential information from a core file. + + + A core dump includes a memory image taken at the time the operating system terminates an application. The memory image could contain sensitive data and is generally useful only for developers trying to debug problems. + + https://www.freedesktop.org/software/systemd/man/coredump.conf.html + + + + +Create or edit the file /etc/systemd/coredump.conf +, or a file in the /etc/systemd/coredump.conf.d + directory ending in .conf +. + +Edit or add the following line in the [Coredump] + section: + Storage=none + + + Example: + + +#!/usr/bin/env bash
+
+{
+ [ ! -d /etc/systemd/coredump.conf.d/ ] && mkdir /etc/systemd/coredump.conf.d/
+ if grep -Psq -- '^\h*\[Coredump\]' /etc/systemd/coredump.conf.d/60-coredump.conf; then
+ printf '%s\n' "Storage=none" >> /etc/systemd/coredump.conf.d/60-coredump.conf
+ else
+ printf '%s\n' "[Coredump]" "Storage=none" >> /etc/systemd/coredump.conf.d/60-coredump.conf
+ fi
+} +
+
+
+
+ + + + + + + +
+
+ + Configure system wide crypto policy + + The crypto policy definition files have a simple syntax following an INI file key = value syntax + +Full policy definition files have suffix .pol +, subpolicy files have suffix .pmod +. Subpolicies do not have to have values set for all the keys. + The effective configuration of a policy with subpolicies applied is the same as a configuration from a single policy obtained by concatenating the policy and the subpolicies in question. + +The policy files shipped in packages are placed in /usr/share/crypto-policies/policies + and the subpolicies in /usr/share/crypto-policies/policies/modules +. + +Locally configured policy files should be placed in /etc/crypto-policies/policies + and subpolicies in /etc/crypto-policies/policies/modules +. + +The policy and subpolicy files must have names in upper-case except for the .pol + and .pmod + suffix as the update-crypto-policies command always converts the policy name to upper-case before searching for the policy on the filesystem. + The following predefined policies are included: + + + DEFAULT + - The default system-wide cryptographic policy level offers secure settings for current threat models. It allows the TLS 1.2 and 1.3 protocols, as well as the IKEv2 and SSH2 protocols. The RSA keys and Diffie-Hellman parameters are accepted if they are at least 2048 bits long. + + LEGACY + - This policy ensures maximum compatibility with Red Hat Enterprise Linux 5 and earlier; it is less secure due to an increased attack surface. In addition to the DEFAULT level algorithms and protocols, it includes support for the TLS 1.0 and 1.1 protocols. The algorithms DSA, 3DES, and RC4 are allowed, while RSA keys and Diffie-Hellman parameters are accepted if they are at least 1023 bits long. + + FUTURE + - A stricter forward-looking security level intended for testing a possible future policy. This policy does not allow the use of SHA-1 in signature algorithms. It allows the TLS 1.2 and 1.3 protocols, as well as the IKEv2 and SSH2 protocols. The RSA keys and Diffie-Hellman parameters are accepted if they are at least 3072 bits long. If your system communicates on the public internet, you might face interoperability problems. + + FIPS + - A policy level that conforms with the FIPS 140 requirements. The fips-mode-setup tool, which switches the RHEL system into FIPS mode, uses this policy internally. Switching to the FIPS policy does not guarantee compliance with the FIPS 140 standard. You also must re-generate all cryptographic keys after you set the system to FIPS mode. This is not possible in many scenarios. + + + + Ensure system wide crypto policy is not set to legacy + + When a system-wide policy is set up, the default behavior of applications will be to follow the policy. Applications will be unable to use algorithms and +protocols that do not meet the policy, unless you explicitly request the application to do so. + The system-wide crypto-policies followed by the crypto core components allow consistently deprecating and disabling algorithms system-wide. + +The LEGACY + policy ensures maximum compatibility with version 5 of the operating system and earlier; it is less secure due to an increased attack surface. In addition to the DEFAULT + level algorithms and protocols, it includes support for the TLS 1.0 + and 1.1 + protocols. The algorithms DSA +, 3DES +, and RC4 + are allowed, while RSA keys + and Diffie-Hellman + parameters are accepted if they are at least 1023 bits long. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + - IF - + FIPS is required by local site policy: + +The system-wide cryptographic policies contain a policy level that enables cryptographic algorithms in accordance with the requirements by the Federal Information Processing Standard (FIPS) Publication 140. The fips-mode-setup tool that enables or disables FIPS mode internally uses the FIPS systemwide cryptographic policy. +Switching the system to FIPS mode by using the FIPS system-wide cryptographic policy does not guarantee compliance with the FIPS 140 standard. Re-generating all cryptographic keys after setting the system to FIPS mode may not be possible. For example, in the case of an existing IdM realm with users' cryptographic keys you cannot re-generate all the keys. The fips-mode-setup tool uses the FIPS policy internally. But on top of what the update-crypto-policies + command with the --set FIPS + option does, fips-mode-setup + ensures the installation of the FIPS dracut + module by using the fips-finish-install + tool, it also adds the fips=1 + boot option to the kernel command line and regenerates the initial ramdisk. + + IMPORTANT: + Only enabling FIPS mode during installation ensures that the system generates all keys with FIPS-approved algorithms and continuous monitoring tests in place. + Run the following command to switch the system to FIPS mode: + # fips-mode-setup --enable + + Output: + +Kernel initramdisks are being regenerated. This might take some time.
+Setting system policy to FIPS
+Note: System-wide crypto policies are applied on application start-up.
+It is recommended to restart the system for the change of policies
+to fully take place.
+FIPS mode will be enabled.
+Please reboot the system for the setting to take effect. +
+ Run the following command to restart the system: + # reboot + + After the reboot has completed, run the following command to verify FIPS mode: + # fips-mode-setup --check + + Output: + FIPS mode is enabled. + +
+
+ + +If the LEGACY + system-wide crypto policy is selected, it includes support for TLS 1.0, TLS 1.1, and SSH2 protocols or later. The algorithms DSA, 3DES, and RC4 are allowed, while RSA and Diffie-Hellman parameters are accepted if larger than 1023-bits. + These legacy protocols and algorithms can make the system vulnerable to attacks, including those listed in RFC 7457 + + + + CRYPTO-POLICIES(7) + https://access.redhat.com/articles/3642912#what-polices-are-provided-1 + fips-mode-setup(8) + NIST SP 800-53 Rev. 5: SC-8 + + + + Run the following command to change the system-wide crypto policy + # update-crypto-policies --set <CRYPTO POLICY> + + + Example: + + # update-crypto-policies --set DEFAULT + + Run the following to make the updated system-wide crypto policy active + # update-crypto-policies + + Impact: + + +Environments that require compatibility with older insecure protocols may require the use +of the less secure LEGACY + policy level. + + + + + + + + + +
+ + Ensure system wide crypto policy is not set in sshd configuration + + System-wide Crypto policy can be over-ridden or opted out of for openSSH + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Over-riding or opting out of the system-wide crypto policy could allow for the use of less secure Ciphers, MACs, KexAlgorithms and GSSAPIKexAlgorithm + + Note: + If changes to the system-wide crypto policy are required to meet local site policy for the openSSH server, these changes should be done with a sub-policy + assigned to the system-wide crypto policy. For additional information see the CRYPTO-POLICIES(7) man page + + + + NIST SP 800-53 Rev. 5: SC-8, IA-5, AC-17 + + + + Run the following commands: + +# sed -ri "s/^\s*(CRYPTO_POLICY\s*=.*)$/# \1/" /etc/sysconfig/sshd
+
+# systemctl reload sshd +
+
+
+
+ + + + + + + + +
+ + Ensure system wide crypto policy disables sha1 hash and signature support + + SHA-1 (Secure Hash Algorithm) is a cryptographic hash function that produces a 160 bit hash value. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + The SHA-1 hash function has an inherently weak design, and advancing cryptanalysis has made it vulnerable to attacks. The most significant danger for a hash algorithm is when a "collision" which happens when two different pieces of data produce the same hash value occurs. This hashing algorithm has been considered weak since 2005. + + Note: + The use of SHA-1 with hashbased message authentication codes (HMAC) do not rely on the collision resistance of the corresponding hash function, and therefore the recent attacks on SHA-1 have a significantly lower impact on the use of SHA-1 for HMAC. Because of this, the recommendation does not disable the hmac-sha1 MAC. + + + + crypto-policies(7) + update-crypto-policies(8) + Red Hat Enterprise 8 security hardening + https://www.redhat.com/en/blog/how-customize-crypto-policies-rhel-82 + https://access.redhat.com/articles/3642912 + NIST SP 800-53 Rev. 5: SC-8 + + + + + Note: + + + +The commands below are written for the included DEFAULT + system-wide crypto policy. If another policy is in use and follows local site policy, replace DEFAULT + with the name of your system-wide crypto policy. + +Multiple subpolicies may be assigned to a policy as a colon separated list. e.g. DEFAULT:NO-SHA1:NO-SSHCBC + + +Subpolicies: + + +Not included in the update-crypto-policies --set + command will not + be applied to the system wide crypto policy. + + must exist + before they can be applied to the system wide crypto policy. + + .pmod + file filenames must be in all upper case, upper case, e.g. NO-SHA1.pmod +, or they will not + be read by the update-crypto-policies --set + command. + + + + +Create or edit a file in /etc/crypto-policies/policies/modules/ + ending in .pmod + and add or modify the following lines: + +hash = -SHA1
+sign = -*-SHA1
+sha1_in_certs = 0 +
+ + Example: + + # printf '%s\n' "# This is a subpolicy dropping the SHA1 hash and signature support" "hash = -SHA1" "sign = -*-SHA1" "sha1_in_certs = 0" >> /etc/crypto-policies/policies/modules/NO-SHA1.pmod + + Run the following command to update the system-wide cryptographic policy + # update-crypto-policies --set <CRYPTO_POLICY>:<CRYPTO_SUBPOLICY1>:<CRYPTO_SUBPOLICY2>:<CRYPTO_SUBPOLICY3> + + + Example: + + update-crypto-policies --set DEFAULT:NO-SHA1 + + Run the following command to reboot the system to make your cryptographic settings effective for already running services and applications: + # reboot + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure system wide crypto policy disables macs less than 128 bits + + Message Authentication Code (MAC) algorithm is a family of cryptographic functions that is parameterized by a symmetric key. Each of the functions can act on input data (called a “message”) of variable length to produce an output value of a specified length. The output value is called the MAC of the input message. + A MAC algorithm can be used to provide data-origin authentication and data-integrity protection + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Weak algorithms continue to have a great deal of attention as a weak spot that can be exploited with expanded computing power. An attacker that breaks the algorithm could take advantage of a MiTM position to decrypt the tunnel and capture credentials and information. + A MAC algorithm must be computationally infeasible to determine the MAC of a message without knowledge of the key, even if one has already seen the results of using that key to compute the MAC's of other messages. + + + + https://nvd.nist.gov/vuln/detail/CVE-2008-5161 + crypto-policies(7) + update-crypto-policies(8) + Red Hat Enterprise 8 security hardening + https://www.redhat.com/en/blog/how-customize-crypto-policies-rhel-82 + https://csrc.nist.gov/glossary/term/message_authentication_code_algorithm + NIST SP 800-53 Rev. 5: SC-8 + + + + + Note: + + + +The commands below are written for the included DEFAULT + system-wide crypto policy. If another policy is in use and follows local site policy, replace DEFAULT + with the name of your system-wide crypto policy. + +Multiple subpolicies may be assigned to a policy as a colon separated list. e.g. DEFAULT:NO-SHA1:NO-SSHCBC + + +Subpolicies: + + +Not included in the update-crypto-policies --set + command will not + be applied to the system wide crypto policy. + + must exist + before they can be applied to the system wide crypto policy. + + .pmod + file filenames must be in all upper case, upper case, e.g. NO-WEAKMAC.pmod +, or they will not + be read by the update-crypto-policies --set + command. + + + + +Create or edit a file in /etc/crypto-policies/policies/modules/ + ending in .pmod + and add or modify one + of the following lines: + mac = -*-64* # Disables weak macs + + + Example: + + # printf '%s\n' "# This is a subpolicy to disable weak macs" "mac = -*-64" >> /etc/crypto-policies/policies/modules/NO-WEAKMAC.pmod + + Run the following command to update the system-wide cryptographic policy + # update-crypto-policies --set <CRYPTO_POLICY>:<CRYPTO_SUBPOLICY1>:<CRYPTO_SUBPOLICY2>:<CRYPTO_SUBPOLICY3> + + + Example: + + update-crypto-policies --set DEFAULT:NO-SHA1:NO-WEAKMAC + + Run the following command to reboot the system to make your cryptographic settings effective for already running services and applications: + # reboot + + + + + + + + + + + + + + + + + + + + + + + + + Ensure system wide crypto policy disables cbc for ssh + + Cypher Block Chaining (CBC) is an algorithm that uses a block cipher. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + A vulnerability exists in SSH messages that employ CBC mode that may allow an attacker to recover plaintext from a block of ciphertext. If exploited, this attack can potentially allow an attacker to recover up to 32 bits of plaintext from an arbitrary block of ciphertext from a connection secured using the SSH protocol. + + + + https://nvd.nist.gov/vuln/detail/CVE-2008-5161 + crypto-policies(7) + update-crypto-policies(8) + Red Hat Enterprise 8 security hardening + https://www.redhat.com/en/blog/how-customize-crypto-policies-rhel-82 + NIST SP 800-53 Rev. 5: SC-8 + + + + + Note: + + + +The commands below are written for the included DEFAULT + system-wide crypto policy. If another policy is in use and follows local site policy, replace DEFAULT + with the name of your system-wide crypto policy. + + CBC + can be turned off globally by using the argument cipher + opposed to cipher@SSH + + +Multiple subpolicies may be assigned to a policy as a colon separated list. e.g. DEFAULT:NO-SHA1:NO-SSHCBC + + +Subpolicies: + + +Not included in the update-crypto-policies --set + command will not + be applied to the system wide crypto policy. + + must exist + before they can be applied to the system wide crypto policy. + + .pmod + file filenames must be in all upper case, upper case, e.g. NO-SSHCBC.pmod +, or they will not + be read by the update-crypto-policies --set + command. +Create or edit a file in /etc/crypto-policies/policies/modules/ + ending in .pmod + and add or modify one + of the the following lines: + + + + cipher@SSH = -*-CBC # Disables the CBC cipher for SSH + + + Example: + + # printf '%s\n' "# This is a subpolicy to disable all CBC mode ciphers" "# for the SSH protocol (libssh and OpenSSH)" "cipher@SSH = -*-CBC" >> /etc/crypto-policies/policies/modules/NO-SSHCBC.pmod + + Run the following command to update the system-wide cryptographic policy + # update-crypto-policies --set <CRYPTO_POLICY>:<CRYPTO_SUBPOLICY1>:<CRYPTO_SUBPOLICY2>:<CRYPTO_SUBPOLICY3> + + + Example: + + update-crypto-policies --set DEFAULT:NO-SHA1:NO-WEAKMAC:NO-SSHCBC + + Run the following command to reboot the system to make your cryptographic settings effective for already running services and applications: + # reboot + + Impact: + + CBC ciphers might be the only common cyphers when connecting to older SSH clients and servers + + + + + + + + + + + + + + + + + + + + Ensure system wide crypto policy disables chacha20-poly1305 for ssh + + ChaCha20-Poly1305 is an authenticated encryption with additional data (AEAD) algorithm, that combines the ChaCha20 stream cipher with the Poly1305 message authentication code. Its usage in IETF protocols is standardized in RFC 8439. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +A vulnerability exists in ChaCha20-Poly1305 as referenced in CVE-2023-48795 + + + + + https://nvd.nist.gov/vuln/detail/CVE-2023-48795 + crypto-policies(7) + update-crypto-policies(8) + Red Hat Enterprise 8 security hardening + https://www.redhat.com/en/blog/how-customize-crypto-policies-rhel-82 + NIST SP 800-53 Rev. 5: SC-8 + + + + + Note: + + + +The commands below are written for the included DEFAULT + system-wide crypto policy. If another policy is in use and follows local site policy, replace DEFAULT + with the name of your system-wide crypto policy. + + chacha20-poly1305 + can be turned off globally by using the argument cipher + opposed to cipher@SSH + + +Multiple subpolicies may be assigned to a policy as a colon separated list. e.g. DEFAULT:NO-SHA1:NO-SSHCBC + + +Subpolicies: + + +Not included in the update-crypto-policies --set + command will not + be applied to the system wide crypto policy. + + must exist + before they can be applied to the system wide crypto policy. + + .pmod + file filenames must be in all upper case, upper case, e.g. NO-SSHCHACHA20.pmod +, or they will not + be read by the update-crypto-policies --set + command. + + + + + - IF - + CVE-2023-48795 + has been addressed, and it meets local site policy, this recommendation may be skipped. + +Create or edit a file in /etc/crypto-policies/policies/modules/ + ending in .pmod + and add or modify one + of the the following lines: + cipher@SSH = -CHACHA20-POLY1305 # Disables the chacha20-poly1305 cipher for SSH + + + Example: + + # printf '%s\n' "# This is a subpolicy to disable the chacha20-poly1305 ciphers" "# for the SSH protocol (libssh and OpenSSH)" "cipher@SSH = -CHACHA20-POLY1305" >> /etc/crypto-policies/policies/modules/NO-SSHCHACHA20.pmod + + Run the following command to update the system-wide cryptographic policy + # update-crypto-policies --set <CRYPTO_POLICY>:<CRYPTO_SUBPOLICY1>:<CRYPTO_SUBPOLICY2>:<CRYPTO_SUBPOLICY3> + + + Example: + + # update-crypto-policies --set DEFAULT:NO-SHA1:NO-WEAKMAC:NO-SSHCBC:NO-SSHCHACHA20 + + Run the following command to reboot the system to make your cryptographic settings effective for already running services and applications: + # reboot + + + + + + + + + + + + + + + + + + + + Ensure system wide crypto policy disables EtM for ssh + + Encrypt-then-MAC (EtM) - The ciphertext is generated by encrypting the plaintext and then appending a MAC of the encrypted plaintext + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +There is an effective attack against SSH's use of Cypher-Block-Chaining (CBC) with Encrypt-then-MAC as referenced in CVE-2023-48795 + + + + + https://nvd.nist.gov/vuln/detail/CVE-2023-48795 + crypto-policies(7) + update-crypto-policies(8) + Red Hat Enterprise 8 security hardening + https://www.redhat.com/en/blog/how-customize-crypto-policies-rhel-82 + NIST SP 800-53 Rev. 5: SC-8 + https://access.redhat.com/errata/RHSA-2024:0499 + + + + + Note: + + + +The commands below are written for the included DEFAULT + system-wide crypto policy. If another policy is in use and follows local site policy, replace DEFAULT + with the name of your system-wide crypto policy. + + EtM + can be turned off globally by using the argument etm + opposed to etm@SSH + + +Multiple subpolicies may be assigned to a policy as a colon separated list. e.g. DEFAULT:NO-SHA1:NO-SSHCBC + + +Subpolicies: + + +Not included in the update-crypto-policies --set + command will not + be applied to the system wide crypto policy. + + must exist + before they can be applied to the system wide crypto policy. + + .pmod + file filenames must be in all upper case, upper case, e.g. NO-SSHCHACHA20.pmod +, or they will not + be read by the update-crypto-policies --set + command. + + + + + - IF - + CVE-2023-48795 + has been addressed, and it meets local site policy, this recommendation may be skipped. + +Create or edit a file in /etc/crypto-policies/policies/modules/ + ending in .pmod + and add or modify the following line: + etm@SSH = DISABLE_ETM # This disables EtM for openSSH and libssh + + + Example: + + # printf '%s\n' "# This is a subpolicy to disable Encrypt then MAC" "# for the SSH protocol (libssh and OpenSSH)" "etm@SSH = DISABLE_ETM" >> /etc/crypto-policies/policies/modules/NO-SSHETM.pmod + + Run the following command to update the system-wide cryptographic policy + # update-crypto-policies --set <CRYPTO_POLICY>:<CRYPTO_SUBPOLICY1>:<CRYPTO_SUBPOLICY2>:<CRYPTO_SUBPOLICY3> + + + Example: + + # update-crypto-policies --set DEFAULT:NO-SHA1:NO-WEAKMAC:NO-SSHCBC:NO-SSHCHACHA20:NO-SSHETM + + Run the following command to reboot the system to make your cryptographic settings effective for already running services and applications: + # reboot + + + + + + + + + + + + + + + + + + + + + + + +
+ + Configure Command Line Warning Banners + + Presenting a warning message prior to the normal user login may assist in the prosecution of trespassers on the computer system. Changing some of these login banners also has the side effect of hiding OS version information and other detailed system information from attackers attempting to target specific exploits at a system. + +Guidelines published by the US Department of Defense require that warning messages include at least the name of the organization that owns the system, the fact that the system is subject to monitoring and that such monitoring is in compliance with local statutes, and that use of the system implies consent to such monitoring. It is important that the organization's legal counsel review the content of all messages before any system modifications are made, as these warning messages are inherently site-specific. More information (including citations of relevant case law) can be found at http://www.justice.gov/criminal/cybercrime/ + + +The /etc/motd +, /etc/issue +, and /etc/issue.net + files govern warning banners for standard command line logins for both local and remote users. + + Note: + The text provided in the remediation actions for these items is intended as an example only. Please edit to include the specific text for your organization as approved by your legal department. + + + Ensure message of the day is configured properly + + +The contents of the /etc/motd + file are displayed to users after login and function as a message of the day for authenticated users. + +Unix-based systems have typically displayed information about the OS release and patch level upon logging in to the system. This information can be useful to developers who are developing software for a particular OS platform. If mingetty(8) + supports the following options, they display operating system information: \m + - machine architecture \r + - operating system release \s + - operating system name \v + - operating system version + + + +Warning messages inform users who are attempting to login to the system of their legal status regarding the system and must include the name of the organization that owns the system and any monitoring policies that are in place. Displaying OS and patch level information in login banners also has the side effect of providing detailed system information to attackers attempting to target specific exploits of a system. Authorized users can easily get this information by running the " uname -a + " command once they have logged in. + + NIST SP 800-53 Rev. 5: CM-6, CM-1, CM-3 + + + + +Edit the file found in /etc/motd.d/* + with the appropriate contents according to your site policy, remove any instances of \m + , \r + , \s + , \v + or references to the OS platform + + + - OR - + + + - IF - + the motd + is not used, this file can be removed. + +Run the following command to remove the motd + file: + # rm /etc/motd + + Run the following script and review and/or update all returned files' contents to: + + +Remove all system information ( \v +, \r +; \m +, \s +) + Remove any refence to the operating system + Ensure contents follow local site policy + + +#!/usr/bin/env bash
+
+{
+ a_files=()
+ for l_file in /etc/motd{,.d/*}; do
+ if grep -Psqi -- "(\\\v|\\\r|\\\m|\\\s|\b$(grep ^ID= /etc/os-release | cut -d= -f2 | sed -e 's/"//g')\b)" "$l_file"; then
+ echo -e "\n - File: \"$l_file\" includes system information. Edit this file to remove these entries"
+ else
+ a_files+=("$l_file")
+ fi
+ done
+ if [ "${#a_files[@]}" -gt 0 ]; then
+ echo -e "\n- ** Please review the following files and verify their contents follow local site policy **\n"
+ printf '%s\n' "${a_files[@]}"
+ fi
+} +
+
+
+
+ + + + + + +
+ + Ensure local login warning banner is configured properly + + +The contents of the /etc/issue + file are displayed to users prior to login for local terminals. + +Unix-based systems have typically displayed information about the OS release and patch level upon logging in to the system. This information can be useful to developers who are developing software for a particular OS platform. If mingetty(8) + supports the following options, they display operating system information: \m + - machine architecture \r + - operating system release \s + - operating system name \v + - operating system version - or the operating system's name + + + +Warning messages inform users who are attempting to login to the system of their legal status regarding the system and must include the name of the organization that owns the system and any monitoring policies that are in place. Displaying OS and patch level information in login banners also has the side effect of providing detailed system information to attackers attempting to target specific exploits of a system. Authorized users can easily get this information by running the " uname -a + " command once they have logged in. + + NIST SP 800-53 Rev. 5: CM-6, CM-1, CM-3 + + + + +Edit the /etc/issue + file with the appropriate contents according to your site policy, remove any instances of \m + , \r + , \s + , \v + or references to the OS platform + + + Example: + + # echo "Authorized users only. All activity may be monitored and reported." > /etc/issue + + + + + + + + + + + + + + + + Ensure remote login warning banner is configured properly + + +The contents of the /etc/issue.net + file are displayed to users prior to login for remote connections from configured services. + +Unix-based systems have typically displayed information about the OS release and patch level upon logging in to the system. This information can be useful to developers who are developing software for a particular OS platform. If mingetty(8) + supports the following options, they display operating system information: \m + - machine architecture \r + - operating system release \s + - operating system name \v + - operating system version + + + +Warning messages inform users who are attempting to login to the system of their legal status regarding the system and must include the name of the organization that owns the system and any monitoring policies that are in place. Displaying OS and patch level information in login banners also has the side effect of providing detailed system information to attackers attempting to target specific exploits of a system. Authorized users can easily get this information by running the " uname -a + " command once they have logged in. + + NIST SP 800-53 Rev. 5: CM-6, CM-1, CM-3 + + + + +Edit the /etc/issue.net + file with the appropriate contents according to your site policy, remove any instances of \m + , \r + , \s + , \v + or references to the OS platform + + + Example: + + # echo "Authorized users only. All activity may be monitored and reported." > /etc/issue.net + + + + + + + + + + + + + + + + Ensure access to /etc/motd is configured + + +The contents of the /etc/motd + file are displayed to users after login and function as a message of the day for authenticated users. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + + - IF - + the /etc/motd + file does not have the correct access configured, it could be modified by unauthorized users with incorrect or misleading information. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/motd +: + +# chown root:root $(readlink -e /etc/motd)
+# chmod u-x,go-wx $(readlink -e /etc/motd) +
+ + - OR - + + +Run the following command to remove the /etc/motd + file: + # rm /etc/motd + +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure access to /etc/issue is configured + + +The contents of the /etc/issue + file are displayed to users prior to login for local terminals. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + + - IF - + the /etc/issue + file does not have the correct access configured, it could be modified by unauthorized users with incorrect or misleading information. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/issue +: + +# chown root:root $(readlink -e /etc/issue)
+# chmod u-x,go-wx $(readlink -e /etc/issue) +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure access to /etc/issue.net is configured + + +The contents of the /etc/issue.net + file are displayed to users prior to login for remote connections from configured services. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + + - IF - + the /etc/issue.net + file does not have the correct access configured, it could be modified by unauthorized users with incorrect or misleading information. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/issue.net +: + +# chown root:root $(readlink -e /etc/issue.net)
+# chmod u-x,go-wx $(readlink -e /etc/issue.net) +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+ + Configure GNOME Display Manager + + The GNOME Display Manager (GDM) is a program that manages graphical display servers and handles graphical user logins. + + Note: + If GDM is not installed on the system, this section can be skipped + + + Ensure GNOME Display Manager is removed + + The GNOME Display Manager (GDM) is a program that manages graphical display servers and handles graphical user logins. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If a Graphical User Interface (GUI) is not required, it should be removed to reduce the attack surface of the system. + + + + https://wiki.gnome.org/Projects/GDM + + + + +Run the following command to remove the gdm + package + # dnf remove gdm + + Impact: + + Removing the GNOME Display manager will remove the Graphical User Interface (GUI) from the system. + + + + + + + + + + + + Ensure GDM login banner is configured + + GDM is the GNOME Display Manager which handles graphical login for GNOME based systems. + + + + +Additional options and sections may appear in the /etc/dconf/db/gdm.d/01-banner-message + file. + If a different GUI login service is in use, consult your documentation and apply an equivalent banner. + + + + Warning messages inform users who are attempting to login to the system of their legal status regarding the system and must include the name of the organization that owns the system and any monitoring policies that are in place. + + https://help.gnome.org/admin/system-admin-guide/stable/login-banner.html.en + + + + Run the following script to verify that the banner message is enabled and set: + +#!/usr/bin/env bash
+
+{
+ l_pkgoutput=""
+ if command -v dpkg-query > /dev/null 2>&1; then
+ l_pq="dpkg-query -W"
+ elif command -v rpm > /dev/null 2>&1; then
+ l_pq="rpm -q"
+ fi
+ l_pcl="gdm gdm3" # Space separated list of packages to check
+ for l_pn in $l_pcl; do
+ $l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n - Package: \"$l_pn\" exists on the system\n - checking configuration"
+ done
+ if [ -n "$l_pkgoutput" ]; then
+
+ l_gdmprofile="gdm" # Set this to desired profile name IaW Local site policy
+ l_bmessage="'Authorized uses only. All activity may be monitored and reported'" # Set to desired banner message
+ if [ ! -f "/etc/dconf/profile/$l_gdmprofile" ]; then
+ echo "Creating profile \"$l_gdmprofile\""
+ echo -e "user-db:user\nsystem-db:$l_gdmprofile\nfile-db:/usr/share/$l_gdmprofile/greeter-dconf-defaults" > /etc/dconf/profile/$l_gdmprofile
+ fi
+ if [ ! -d "/etc/dconf/db/$l_gdmprofile.d/" ]; then
+ echo "Creating dconf database directory \"/etc/dconf/db/$l_gdmprofile.d/\""
+ mkdir /etc/dconf/db/$l_gdmprofile.d/
+ fi
+ if ! grep -Piq '^\h*banner-message-enable\h*=\h*true\b' /etc/dconf/db/$l_gdmprofile.d/*; then
+ echo "creating gdm keyfile for machine-wide settings"
+ if ! grep -Piq -- '^\h*banner-message-enable\h*=\h*' /etc/dconf/db/$l_gdmprofile.d/*; then
+ l_kfile="/etc/dconf/db/$l_gdmprofile.d/01-banner-message"
+ echo -e "\n[org/gnome/login-screen]\nbanner-message-enable=true" >> "$l_kfile"
+ else
+ l_kfile="$(grep -Pil -- '^\h*banner-message-enable\h*=\h*' /etc/dconf/db/$l_gdmprofile.d/*)"
+ ! grep -Pq '^\h*\[org\/gnome\/login-screen\]' "$l_kfile" && sed -ri '/^\s*banner-message-enable/ i\[org/gnome/login-screen]' "$l_kfile"
+ ! grep -Pq '^\h*banner-message-enable\h*=\h*true\b' "$l_kfile" && sed -ri 's/^\s*(banner-message-enable\s*=\s*)(\S+)(\s*.*$)/\1true \3//' "$l_kfile"
+ # sed -ri '/^\s*\[org\/gnome\/login-screen\]/ a\\nbanner-message-enable=true' "$l_kfile"
+ fi
+ fi
+ if ! grep -Piq "^\h*banner-message-text=[\'\"]+\S+" "$l_kfile"; then
+ sed -ri "/^\s*banner-message-enable/ a\banner-message-text=$l_bmessage" "$l_kfile"
+ fi
+ dconf update
+ else
+ echo -e "\n\n - GNOME Desktop Manager isn't installed\n - Recommendation is Not Applicable\n - No remediation required\n"
+ fi
+} +
+ + Note: + + + + There is no character limit for the banner message. gnome-shell autodetects longer stretches of text and enters two column mode. + + + The banner message cannot be read from an external file. + + - OR - + + + + Run the following command to remove the gdm package: + # dnf remove gdm + +
+
+
+ + + + + + + + + +
+ + Ensure GDM disable-user-list option is enabled + + GDM is the GNOME Display Manager which handles graphical login for GNOME based systems. + +The disable-user-list + option controls if a list of users is displayed on the login screen + + + + If a different GUI login service is in use and required on the system, consult your documentation to disable displaying the user list + + + + Displaying the user list eliminates half of the Userid/Password equation that an unauthorized person would need to log on. + + https://help.gnome.org/admin/system-admin-guide/stable/login-userlist-disable.html.en + + + + +Run the following script to enable the disable-user-list + option: + + Note: + the l_gdm_profile + variable in the script can be changed if a different profile name is desired in accordance with local site policy. + +#!/usr/bin/env bash
+
+{
+ l_gdmprofile="gdm"
+ if [ ! -f "/etc/dconf/profile/$l_gdmprofile" ]; then
+ echo "Creating profile \"$l_gdmprofile\""
+ echo -e "user-db:user\nsystem-db:$l_gdmprofile\nfile-db:/usr/share/$l_gdmprofile/greeter-dconf-defaults" > /etc/dconf/profile/$l_gdmprofile
+ fi
+ if [ ! -d "/etc/dconf/db/$l_gdmprofile.d/" ]; then
+ echo "Creating dconf database directory \"/etc/dconf/db/$l_gdmprofile.d/\""
+ mkdir /etc/dconf/db/$l_gdmprofile.d/
+ fi
+ if ! grep -Piq '^\h*disable-user-list\h*=\h*true\b' /etc/dconf/db/$l_gdmprofile.d/*; then
+ echo "creating gdm keyfile for machine-wide settings"
+ if ! grep -Piq -- '^\h*\[org\/gnome\/login-screen\]' /etc/dconf/db/$l_gdmprofile.d/*; then
+ echo -e "\n[org/gnome/login-screen]\n# Do not show the user list\ndisable-user-list=true" >> /etc/dconf/db/$l_gdmprofile.d/00-login-screen
+ else
+ sed -ri '/^\s*\[org\/gnome\/login-screen\]/ a\# Do not show the user list\ndisable-user-list=true' $(grep -Pil -- '^\h*\[org\/gnome\/login-screen\]' /etc/dconf/db/$l_gdmprofile.d/*)
+ fi
+ fi
+ dconf update
+} +
+ + Note: + When the user profile is created or changed, the user will need to log out and log in again before the changes will be applied. + + - OR - + + Run the following command to remove the GNOME package: + # dnf remove gdm + +
+
+
+ + + + + + + + + +
+ + Ensure GDM screen locks when the user is idle + + GNOME Desktop Manager can make the screen lock automatically whenever the user is idle for some amount of time. + + + idle-delay=uint32 {n} + - Number of seconds of inactivity before the screen goes blank + + lock-delay=uint32 {n} + - Number of seconds after the screen is blank before locking the screen + + + Example key file: + + +# Specify the dconf path
+[org/gnome/desktop/session]
+
+# Number of seconds of inactivity before the screen goes blank
+# Set to 0 seconds if you want to deactivate the screensaver.
+idle-delay=uint32 900
+
+# Specify the dconf path
+[org/gnome/desktop/screensaver]
+
+# Number of seconds after the screen is blank before locking the screen
+lock-delay=uint32 5 +
+
+ + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Setting a lock-out value reduces the window of opportunity for unauthorized user access to another user's session that has been left unattended. + + + + https://help.gnome.org/admin/system-admin-guide/stable/desktop-lockscreen.html.en + + + + +Create or edit a file in the /etc/dconf/profile/ + and verify it includes the following: + +user-db:user
+system-db:{NAME_OF_DCONF_DATABASE} +
+ + Note: + local + is the name of a dconf database used in the examples. + + Example: + + # echo -e '\nuser-db:user\nsystem-db:local' >> /etc/dconf/profile/user + + +Create the directory /etc/dconf/db/{NAME_OF_DCONF_DATABASE}.d/ + if it doesn't already exist: + + Example: + + # mkdir /etc/dconf/db/local.d + + +Create the key file /etc/dconf/db/{NAME_OF_DCONF_DATABASE}.d/{FILE_NAME} + to provide information for the {NAME_OF_DCONF_DATABASE} + database: + + Example script: + + +#!/usr/bin/env bash
+
+{
+ l_key_file="/etc/dconf/db/local.d/00-screensaver"
+ l_idmv="900" # Set max value for idle-delay in seconds (between 1 and 900)
+ l_ldmv="5" # Set max value for lock-delay in seconds (between 0 and 5)
+ {
+ echo '# Specify the dconf path'
+ echo '[org/gnome/desktop/session]'
+ echo ''
+ echo '# Number of seconds of inactivity before the screen goes blank'
+ echo '# Set to 0 seconds if you want to deactivate the screensaver.'
+ echo "idle-delay=uint32 $l_idmv"
+ echo ''
+ echo '# Specify the dconf path'
+ echo '[org/gnome/desktop/screensaver]'
+ echo ''
+ echo '# Number of seconds after the screen is blank before locking the screen'
+ echo "lock-delay=uint32 $l_ldmv"
+ } > "$l_key_file"
+} +
+ + Note: + You must include the uint32 along with the integer key values as shown. + Run the following command to update the system databases: + # dconf update + + + Note: + Users must log out and back in again before the system-wide settings take effect. +
+
+
+ + + + + + + + + + +
+ + Ensure GDM screen locks cannot be overridden + + GNOME Desktop Manager can make the screen lock automatically whenever the user is idle for some amount of time. + By using the lockdown mode in dconf, you can prevent users from changing specific settings. + To lock down a dconf key or subpath, create a locks subdirectory in the keyfile directory. The files inside this directory contain a list of keys or subpaths to lock. Just as with the keyfiles, you may add any number of files to this directory. + + Example Lock File: + + +# Lock desktop screensaver settings
+/org/gnome/desktop/session/idle-delay
+/org/gnome/desktop/screensaver/lock-delay +
+
+ + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Setting a lock-out value reduces the window of opportunity for unauthorized user access to another user's session that has been left unattended. + Without locking down the system settings, user settings take precedence over the system settings. + + + + https://help.gnome.org/admin/system-admin-guide/stable/desktop-lockscreen.html.en + https://help.gnome.org/admin/system-admin-guide/stable/dconf-lockdown.html.en + + + + Run the following script to ensure screen locks cannot be overridden: + +#!/usr/bin/env bash
+
+{
+ # Check if GNMOE Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
+ # determine system's package manager
+ l_pkgoutput=""
+ if command -v dpkg-query > /dev/null 2>&1; then
+ l_pq="dpkg-query -W"
+ elif command -v rpm > /dev/null 2>&1; then
+ l_pq="rpm -q"
+ fi
+ # Check if GDM is installed
+ l_pcl="gdm gdm3" # Space separated list of packages to check
+ for l_pn in $l_pcl; do
+ $l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="y" && echo -e "\n - Package: \"$l_pn\" exists on the system\n - remediating configuration if needed"
+ done
+ # Check configuration (If applicable)
+ if [ -n "$l_pkgoutput" ]; then
+ # Look for idle-delay to determine profile in use, needed for remaining tests
+ l_kfd="/etc/dconf/db/$(grep -Psril '^\h*idle-delay\h*=\h*uint32\h+\d+\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
+ # Look for lock-delay to determine profile in use, needed for remaining tests
+ l_kfd2="/etc/dconf/db/$(grep -Psril '^\h*lock-delay\h*=\h*uint32\h+\d+\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
+ if [ -d "$l_kfd" ]; then # If key file directory doesn't exist, options can't be locked
+ if grep -Prilq '^\h*\/org\/gnome\/desktop\/session\/idle-delay\b' "$l_kfd"; then
+ echo " - \"idle-delay\" is locked in \"$(grep -Pril '^\h*\/org\/gnome\/desktop\/session\/idle-delay\b' "$l_kfd")\""
+ else
+ echo "creating entry to lock \"idle-delay\""
+ [ ! -d "$l_kfd"/locks ] && echo "creating directory $l_kfd/locks" && mkdir "$l_kfd"/locks
+ {
+ echo -e '\n# Lock desktop screensaver idle-delay setting'
+ echo '/org/gnome/desktop/session/idle-delay'
+ } >> "$l_kfd"/locks/00-screensaver
+ fi
+ else
+ echo -e " - \"idle-delay\" is not set so it can not be locked\n - Please follow Recommendation \"Ensure GDM screen locks when the user is idle\" and follow this Recommendation again"
+ fi
+ if [ -d "$l_kfd2" ]; then # If key file directory doesn't exist, options can't be locked
+ if grep -Prilq '^\h*\/org\/gnome\/desktop\/screensaver\/lock-delay\b' "$l_kfd2"; then
+ echo " - \"lock-delay\" is locked in \"$(grep -Pril '^\h*\/org\/gnome\/desktop\/screensaver\/lock-delay\b' "$l_kfd2")\""
+ else
+ echo "creating entry to lock \"lock-delay\""
+ [ ! -d "$l_kfd2"/locks ] && echo "creating directory $l_kfd2/locks" && mkdir "$l_kfd2"/locks
+ {
+ echo -e '\n# Lock desktop screensaver lock-delay setting'
+ echo '/org/gnome/desktop/screensaver/lock-delay'
+ } >> "$l_kfd2"/locks/00-screensaver
+ fi
+ else
+ echo -e " - \"lock-delay\" is not set so it can not be locked\n - Please follow Recommendation \"Ensure GDM screen locks when the user is idle\" and follow this Recommendation again"
+ fi
+ else
+ echo -e " - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
+ fi
+} +
+ Run the following command to update the system databases: + # dconf update + + + Note: + Users must log out and back in again before the system-wide settings take effect. +
+
+
+ + + + + + + + + +
+ + Ensure GDM automatic mounting of removable media is disabled + + By default GNOME automatically mounts removable media when inserted as a convenience to the user. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + With automounting enabled anyone with physical access could attach a USB drive or disc and have its contents available in system even if they lacked permissions to mount it themselves. + + + + https://access.redhat.com/solutions/20107 + + + + Run the following script to disable automatic mounting of media for all GNOME users: + +#!/usr/bin/env bash
+
+{
+ l_pkgoutput=""
+ l_gpname="local" # Set to desired dconf profile name (default is local)
+ # Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
+ # determine system's package manager
+ if command -v dpkg-query > /dev/null 2>&1; then
+ l_pq="dpkg-query -W"
+ elif command -v rpm > /dev/null 2>&1; then
+ l_pq="rpm -q"
+ fi
+ # Check if GDM is installed
+ l_pcl="gdm gdm3" # Space separated list of packages to check
+ for l_pn in $l_pcl; do
+ $l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n - Package: \"$l_pn\" exists on the system\n - checking configuration"
+ done
+ # Check configuration (If applicable)
+ if [ -n "$l_pkgoutput" ]; then
+ echo -e "$l_pkgoutput"
+ # Look for existing settings and set variables if they exist
+ l_kfile="$(grep -Prils -- '^\h*automount\b' /etc/dconf/db/*.d)"
+ l_kfile2="$(grep -Prils -- '^\h*automount-open\b' /etc/dconf/db/*.d)"
+ # Set profile name based on dconf db directory ({PROFILE_NAME}.d)
+ if [ -f "$l_kfile" ]; then
+ l_gpname="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile")"
+ echo " - updating dconf profile name to \"$l_gpname\""
+ elif [ -f "$l_kfile2" ]; then
+ l_gpname="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile2")"
+ echo " - updating dconf profile name to \"$l_gpname\""
+ fi
+ # check for consistency (Clean up configuration if needed)
+ if [ -f "$l_kfile" ] && [ "$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile")" != "$l_gpname" ]; then
+ sed -ri "/^\s*automount\s*=/s/^/# /" "$l_kfile"
+ l_kfile="/etc/dconf/db/$l_gpname.d/00-media-automount"
+ fi
+ if [ -f "$l_kfile2" ] && [ "$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile2")" != "$l_gpname" ]; then
+ sed -ri "/^\s*automount-open\s*=/s/^/# /" "$l_kfile2"
+ fi
+ [ -z "$l_kfile" ] && l_kfile="/etc/dconf/db/$l_gpname.d/00-media-automount"
+ # Check if profile file exists
+ if grep -Pq -- "^\h*system-db:$l_gpname\b" /etc/dconf/profile/*; then
+ echo -e "\n - dconf database profile exists in: \"$(grep -Pl -- "^\h*system-db:$l_gpname\b" /etc/dconf/profile/*)\""
+ else
+ if [ ! -f "/etc/dconf/profile/user" ]; then
+ l_gpfile="/etc/dconf/profile/user"
+ else
+ l_gpfile="/etc/dconf/profile/user2"
+ fi
+ echo -e " - creating dconf database profile"
+ {
+ echo -e "\nuser-db:user"
+ echo "system-db:$l_gpname"
+ } >> "$l_gpfile"
+ fi
+ # create dconf directory if it doesn't exists
+ l_gpdir="/etc/dconf/db/$l_gpname.d"
+ if [ -d "$l_gpdir" ]; then
+ echo " - The dconf database directory \"$l_gpdir\" exists"
+ else
+ echo " - creating dconf database directory \"$l_gpdir\""
+ mkdir "$l_gpdir"
+ fi
+ # check automount-open setting
+ if grep -Pqs -- '^\h*automount-open\h*=\h*false\b' "$l_kfile"; then
+ echo " - \"automount-open\" is set to false in: \"$l_kfile\""
+ else
+ echo " - creating \"automount-open\" entry in \"$l_kfile\""
+ ! grep -Psq -- '\^\h*\[org\/gnome\/desktop\/media-handling\]\b' "$l_kfile" && echo '[org/gnome/desktop/media-handling]' >> "$l_kfile"
+ sed -ri '/^\s*\[org\/gnome\/desktop\/media-handling\]/a \\nautomount-open=false' "$l_kfile"
+ fi
+ # check automount setting
+ if grep -Pqs -- '^\h*automount\h*=\h*false\b' "$l_kfile"; then
+ echo " - \"automount\" is set to false in: \"$l_kfile\""
+ else
+ echo " - creating \"automount\" entry in \"$l_kfile\""
+ ! grep -Psq -- '\^\h*\[org\/gnome\/desktop\/media-handling\]\b' "$l_kfile" && echo '[org/gnome/desktop/media-handling]' >> "$l_kfile"
+ sed -ri '/^\s*\[org\/gnome\/desktop\/media-handling\]/a \\nautomount=false' "$l_kfile"
+ fi
+ # update dconf database
+ dconf update
+ else
+ echo -e "\n - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
+ fi
+} +
+ + - OR - + + Run the following command to uninstall the GNOME desktop Manager package: + # dnf remove gdm + + Impact: + + The use of portable hard drives is very common for workstation users. If your organization allows the use of portable storage or media on workstations and physical access controls to workstations is considered adequate there is little value add in turning off automounting. + +
+
+
+ + + + + + + + + +
+ + Ensure GDM disabling automatic mounting of removable media is not overridden + + By default GNOME automatically mounts removable media when inserted as a convenience to the user + By using the lockdown mode in dconf, you can prevent users from changing specific settings. + To lock down a dconf key or subpath, create a locks subdirectory in the keyfile directory. The files inside this directory contain a list of keys or subpaths to lock. Just as with the keyfiles, you may add any number of files to this directory. + + Example Lock File: + + +# Lock automount settings
+/org/gnome/desktop/media-handling/automount
+/org/gnome/desktop/media-handling/automount-open +
+
+ + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + With automounting enabled anyone with physical access could attach a USB drive or disc and have its contents available in system even if they lacked permissions to mount it themselves. + + + + https://help.gnome.org/admin/system-admin-guide/stable/dconf-lockdown.html.en + + + + Run the following script to lock disable automatic mounting of media for all GNOME users: + +#!/usr/bin/env bash
+
+{
+ # Check if GNMOE Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
+ # determine system's package manager
+ l_pkgoutput=""
+ if command -v dpkg-query > /dev/null 2>&1; then
+ l_pq="dpkg-query -W"
+ elif command -v rpm > /dev/null 2>&1; then
+ l_pq="rpm -q"
+ fi
+ # Check if GDM is installed
+ l_pcl="gdm gdm3" # Space separated list of packages to check
+ for l_pn in $l_pcl; do
+ $l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="y" && echo -e "\n - Package: \"$l_pn\" exists on the system\n - remediating configuration if needed"
+ done
+ # Check configuration (If applicable)
+ if [ -n "$l_pkgoutput" ]; then
+ # Look for automount to determine profile in use, needed for remaining tests
+ l_kfd="/etc/dconf/db/$(grep -Psril '^\h*automount\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
+ # Look for automount-open to determine profile in use, needed for remaining tests
+ l_kfd2="/etc/dconf/db/$(grep -Psril '^\h*automount-open\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
+ if [ -d "$l_kfd" ]; then # If key file directory doesn't exist, options can't be locked
+ if grep -Priq '^\h*\/org/gnome\/desktop\/media-handling\/automount\b' "$l_kfd"; then
+ echo " - \"automount\" is locked in \"$(grep -Pril '^\h*\/org/gnome\/desktop\/media-handling\/automount\b' "$l_kfd")\""
+ else
+ echo " - creating entry to lock \"automount\""
+ [ ! -d "$l_kfd"/locks ] && echo "creating directory $l_kfd/locks" && mkdir "$l_kfd"/locks
+ {
+ echo -e '\n# Lock desktop media-handling automount setting'
+ echo '/org/gnome/desktop/media-handling/automount'
+ } >> "$l_kfd"/locks/00-media-automount
+ fi
+ else
+ echo -e " - \"automount\" is not set so it can not be locked\n - Please follow Recommendation \"Ensure GDM automatic mounting of removable media is disabled\" and follow this Recommendation again"
+ fi
+ if [ -d "$l_kfd2" ]; then # If key file directory doesn't exist, options can't be locked
+ if grep -Priq '^\h*\/org/gnome\/desktop\/media-handling\/automount-open\b' "$l_kfd2"; then
+ echo " - \"automount-open\" is locked in \"$(grep -Pril '^\h*\/org/gnome\/desktop\/media-handling\/automount-open\b' "$l_kfd2")\""
+ else
+ echo " - creating entry to lock \"automount-open\""
+ [ ! -d "$l_kfd2"/locks ] && echo "creating directory $l_kfd2/locks" && mkdir "$l_kfd2"/locks
+ {
+ echo -e '\n# Lock desktop media-handling automount-open setting'
+ echo '/org/gnome/desktop/media-handling/automount-open'
+ } >> "$l_kfd2"/locks/00-media-automount
+ fi
+ else
+ echo -e " - \"automount-open\" is not set so it can not be locked\n - Please follow Recommendation \"Ensure GDM automatic mounting of removable media is disabled\" and follow this Recommendation again"
+ fi
+ # update dconf database
+ dconf update
+ else
+ echo -e " - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
+ fi
+} +
+ Impact: + + The use of portable hard drives is very common for workstation users + +
+
+
+ + + + + + + + + +
+ + Ensure GDM autorun-never is enabled + + +The autorun-never + setting allows the GNOME Desktop Display Manager to disable autorun through GDM. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Malware on removable media may take advantage of Autorun features when the media is inserted into a system and execute. + + + + + + + +Run the following script to set autorun-never + to true + for GDM users: + +#!/usr/bin/env bash
+
+{
+ l_pkgoutput="" l_output="" l_output2=""
+ l_gpname="local" # Set to desired dconf profile name (default is local)
+ # Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
+ # determine system's package manager
+ if command -v dpkg-query > /dev/null 2>&1; then
+ l_pq="dpkg-query -W"
+ elif command -v rpm > /dev/null 2>&1; then
+ l_pq="rpm -q"
+ fi
+ # Check if GDM is installed
+ l_pcl="gdm gdm3" # Space separated list of packages to check
+ for l_pn in $l_pcl; do
+ $l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="$l_pkgoutput\n - Package: \"$l_pn\" exists on the system\n - checking configuration"
+ done
+ echo -e "$l_pkgoutput"
+ # Check configuration (If applicable)
+ if [ -n "$l_pkgoutput" ]; then
+ echo -e "$l_pkgoutput"
+ # Look for existing settings and set variables if they exist
+ l_kfile="$(grep -Prils -- '^\h*autorun-never\b' /etc/dconf/db/*.d)"
+ # Set profile name based on dconf db directory ({PROFILE_NAME}.d)
+ if [ -f "$l_kfile" ]; then
+ l_gpname="$(awk -F\/ '{split($(NF-1),a,".");print a[1]}' <<< "$l_kfile")"
+ echo " - updating dconf profile name to \"$l_gpname\""
+ fi
+ [ ! -f "$l_kfile" ] && l_kfile="/etc/dconf/db/$l_gpname.d/00-media-autorun"
+ # Check if profile file exists
+ if grep -Pq -- "^\h*system-db:$l_gpname\b" /etc/dconf/profile/*; then
+ echo -e "\n - dconf database profile exists in: \"$(grep -Pl -- "^\h*system-db:$l_gpname\b" /etc/dconf/profile/*)\""
+ else
+ [ ! -f "/etc/dconf/profile/user" ] && l_gpfile="/etc/dconf/profile/user" || l_gpfile="/etc/dconf/profile/user2"
+ echo -e " - creating dconf database profile"
+ {
+ echo -e "\nuser-db:user"
+ echo "system-db:$l_gpname"
+ } >> "$l_gpfile"
+ fi
+ # create dconf directory if it doesn't exists
+ l_gpdir="/etc/dconf/db/$l_gpname.d"
+ if [ -d "$l_gpdir" ]; then
+ echo " - The dconf database directory \"$l_gpdir\" exists"
+ else
+ echo " - creating dconf database directory \"$l_gpdir\""
+ mkdir "$l_gpdir"
+ fi
+ # check autorun-never setting
+ if grep -Pqs -- '^\h*autorun-never\h*=\h*true\b' "$l_kfile"; then
+ echo " - \"autorun-never\" is set to true in: \"$l_kfile\""
+ else
+ echo " - creating or updating \"autorun-never\" entry in \"$l_kfile\""
+ if grep -Psq -- '^\h*autorun-never' "$l_kfile"; then
+ sed -ri 's/(^\s*autorun-never\s*=\s*)(\S+)(\s*.*)$/\1true \3/' "$l_kfile"
+ else
+ ! grep -Psq -- '\^\h*\[org\/gnome\/desktop\/media-handling\]\b' "$l_kfile" && echo '[org/gnome/desktop/media-handling]' >> "$l_kfile"
+ sed -ri '/^\s*\[org\/gnome\/desktop\/media-handling\]/a \\nautorun-never=true' "$l_kfile"
+ fi
+ fi
+ else
+ echo -e "\n - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
+ fi
+ # update dconf database
+ dconf update
+} +
+
+
+
+ + + + + + + + + +
+ + Ensure GDM autorun-never is not overridden + + The autorun-never setting allows the GNOME Desktop Display Manager to disable autorun through GDM. + By using the lockdown mode in dconf, you can prevent users from changing specific settings. + To lock down a dconf key or subpath, create a locks subdirectory in the keyfile directory. The files inside this directory contain a list of keys or subpaths to lock. Just as with the keyfiles, you may add any number of files to this directory. + Example Lock File: + +# Lock desktop media-handling settings
+/org/gnome/desktop/media-handling/autorun-never +
+
+ + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Malware on removable media may take advantage of Autorun features when the media is inserted into a system and execute. + + + + + + + +Run the following script to ensure that autorun-never=true + cannot be overridden: + +#!/usr/bin/env bash
+
+{
+ # Check if GNOME Desktop Manager is installed. If package isn't installed, recommendation is Not Applicable\n
+ # determine system's package manager
+ l_pkgoutput=""
+ if command -v dpkg-query > /dev/null 2>&1; then
+ l_pq="dpkg-query -W"
+ elif command -v rpm > /dev/null 2>&1; then
+ l_pq="rpm -q"
+ fi
+ # Check if GDM is installed
+ l_pcl="gdm gdm3" # Space separated list of packages to check
+ for l_pn in $l_pcl; do
+ $l_pq "$l_pn" > /dev/null 2>&1 && l_pkgoutput="y" && echo -e "\n - Package: \"$l_pn\" exists on the system\n - remediating configuration if needed"
+ done
+ # Check configuration (If applicable)
+ if [ -n "$l_pkgoutput" ]; then
+ # Look for autorun to determine profile in use, needed for remaining tests
+ l_kfd="/etc/dconf/db/$(grep -Psril '^\h*autorun-never\b' /etc/dconf/db/*/ | awk -F'/' '{split($(NF-1),a,".");print a[1]}').d" #set directory of key file to be locked
+ if [ -d "$l_kfd" ]; then # If key file directory doesn't exist, options can't be locked
+ if grep -Priq '^\h*\/org/gnome\/desktop\/media-handling\/autorun-never\b' "$l_kfd"; then
+ echo " - \"autorun-never\" is locked in \"$(grep -Pril '^\h*\/org/gnome\/desktop\/media-handling\/autorun-never\b' "$l_kfd")\""
+ else
+ echo " - creating entry to lock \"autorun-never\""
+ [ ! -d "$l_kfd"/locks ] && echo "creating directory $l_kfd/locks" && mkdir "$l_kfd"/locks
+ {
+ echo -e '\n# Lock desktop media-handling autorun-never setting'
+ echo '/org/gnome/desktop/media-handling/autorun-never'
+ } >> "$l_kfd"/locks/00-media-autorun
+ fi
+ else
+ echo -e " - \"autorun-never\" is not set so it can not be locked\n - Please follow Recommendation \"Ensure GDM autorun-never is enabled\" and follow this Recommendation again"
+ fi
+ # update dconf database
+ dconf update
+ else
+ echo -e " - GNOME Desktop Manager package is not installed on the system\n - Recommendation is not applicable"
+ fi
+} +
+
+
+
+ + + + + + + + + +
+ + Ensure XDMCP is not enabled + + X Display Manager Control Protocol (XDMCP) is designed to provide authenticated access to display management services for remote displays + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + XDMCP is inherently insecure. + + XDMCP is not a ciphered protocol. This may allow an attacker to capture keystrokes entered by a user + XDMCP is vulnerable to man-in-the-middle attacks. This may allow an attacker to steal the credentials of legitimate users by impersonating the XDMCP server. + + + + + + + + +Edit the file /etc/gdm/custom.conf + and remove the line: + Enable=true + + + + + + + + + + +
+
+ + Services + + While applying system updates and patches helps correct known vulnerabilities, one of the best ways to protect the system against as yet unreported vulnerabilities is to disable all services that are not required for normal system operation. This prevents the exploitation of vulnerabilities discovered at a later date. If a service is not enabled, it cannot be exploited. The actions in this section of the document provide guidance on some services which can be safely disabled and under which circumstances, greatly reducing the number of possible threats to the resulting system. Additionally, some services which should remain enabled but with secure configuration are covered as well as insecure service clients. + + + Configure Server Services + + This section describes services that are installed on systems that specifically need to run these services. If any of these services are not required, it is recommended that the package be removed. + + - IF - + the package is required for a dependency: + + Ensure the dependent package is approved by local site policy + Ensure stopping and masking the service and/or socket meets local site policy + Stop and mask the service and/or socket to reduce the potential attack surface + + The following commands can be used to stop and mask the service and socket: + +# systemctl stop <service_name>.socket <service_name>.service
+# systemctl mask <service_name>.socket <service_name>.service +
+ + Note: + This should not be considered a comprehensive list of services not required for normal system operation. You may wish to consider additions to those listed here for your environment +
+ + Ensure autofs services are not in use + + + autofs + allows automatic mounting of devices, typically including CD/DVDs and USB drives. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + This control should align with the tolerance of the use of portable drives and optical media in the organization. On a server requiring an admin to manually mount media can be part of defense-in-depth to reduce the risk of unapproved software or information being introduced or proprietary software or information being exfiltrated. If admins commonly use flash drives and Server access has sufficient physical controls, requiring manual mounting may not increase security. + + + + With automounting enabled anyone with physical access could attach a USB drive or disc and have its contents available in system even if they lacked permissions to mount it themselves. + + + + NIST SP 800-53 Rev. 5: SI-3, MP-7 + + + + +Run the following commands to stop autofs.service + and remove autofs + package: + +# systemctl stop autofs.service
+# dnf remove autofs +
+ + - OR - + + + - IF - + the autofs + package is required as a dependency: + +Run the following commands to stop and mask autofs.service +: + +# systemctl stop autofs.service
+# systemctl mask autofs.service +
+ Impact: + + The use of portable hard drives is very common for workstation users. If your organization allows the use of portable storage or media on workstations and physical access controls to workstations is considered adequate there is little value add in turning off automounting. + +There may be packages that are dependent on the autofs + package. If the autofs + package is removed, these dependent packages will be removed as well. Before removing the autofs + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the autofs.service + leaving the autofs + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure avahi daemon services are not in use + + Avahi is a free zeroconf implementation, including a system for multicast DNS/DNS-SD service discovery. Avahi allows programs to publish and discover services and hosts running on a local network with no specific configuration. For example, a user can plug a computer into a network and Avahi automatically finds printers to print to, files to look at and people to talk to, as well as network services running on the machine. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Automatic discovery of network services is not normally required for system functionality. It is recommended to remove this package to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: SI-4 + + + + +Run the following commands to stop avahi-daemon.socket + and avahi-daemon.service +, and remove the avahi + package: + +# systemctl stop avahi-daemon.socket avahi-daemon.service
+# dnf remove avahi +
+ + - OR - + + + - IF - + the avahi + package is required as a dependency: + +Run the following commands to stop and mask the avahi-daemon.socket + and avahi-daemon.service +: + +# systemctl stop avahi-daemon.socket avahi-daemon.service
+# systemctl mask avahi-daemon.socket avahi-daemon.service +
+ Impact: + + +There may be packages that are dependent on the avahi + package. If the avahi + package is removed, these dependent packages will be removed as well. Before removing the avahi + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the avahi-daemon.socket + and avahi-daemon.service + leaving the avahi + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure dhcp server services are not in use + + +The Dynamic Host Configuration Protocol (DHCP) is a service that allows machines to be dynamically assigned IP addresses. There are two versions of the DHCP protocol DHCPv4 + and DHCPv6 +. At startup the server may be started for one or the other via the -4 + or -6 + arguments. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Unless a system is specifically set up to act as a DHCP server, it is recommended that the dhcp-server + package be removed to reduce the potential attack surface. + + + + dhcpd(8) + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop dhcpd.service + and dhcpd6.service + and remove dhcp-server + package: + +# systemctl stop dhcpd.service dhcpd6.service
+# dnf remove dhcp-server +
+ + - OR - + + + - IF - + the dhcp-server + package is required as a dependency: + +Run the following commands to stop and mask dhcpd.service + and dhcpd6.service +: + +# systemctl stop dhcpd.service dhcpd6.service
+# systemctl mask dhcpd.service dhcpd6.service +
+ Impact: + + +There may be packages that are dependent on the dhcp-server + package. If the dhcp-server + package is removed, these dependent packages will be removed as well. Before removing the dhcp-server + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the dhcpd.service + and dhcpd6.service + leaving the dhcp-server + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure dns server services are not in use + + The Domain Name System (DNS) is a hierarchical naming system that maps names to IP addresses for computers, services and other resources connected to a network. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless a system is specifically designated to act as a DNS server, it is recommended that the package be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop named.service + and remove bind + package: + +# systemctl stop named.service
+# dnf remove bind +
+ + - OR - + + + - IF - + the bind + package is required as a dependency: + +Run the following commands to stop and mask named.service +: + +# systemctl stop named.service
+# systemctl mask named.service +
+ Impact: + + +There may be packages that are dependent on the bind + package. If the bind + package is removed, these dependent packages will be removed as well. Before removing the bind + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the named.service + leaving the bind + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure dnsmasq services are not in use + + + dnsmasq + is a lightweight tool that provides DNS caching, DNS forwarding and DHCP (Dynamic Host Configuration Protocol) services. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless a system is specifically designated to act as a DNS caching, DNS forwarding and/or DHCP server, it is recommended that the package be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop dnsmasq.service + and remove dnsmasq + package: + +# systemctl stop dnsmasq.service
+# dnf remove dnsmasq +
+ + - OR - + + + - IF - + the dnsmasq + package is required as a dependency: + +Run the following commands to stop and mask the dnsmasq.service +: + +# systemctl stop dnsmasq.service
+# systemctl mask dnsmasq.service +
+ Impact: + + +There may be packages that are dependent on the dnsmasq + package. If the dnsmasq + package is removed, these dependent packages will be removed as well. Before removing the dnsmasq + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the dnsmasq.service + leaving the dnsmasq + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure samba file server services are not in use + + The Samba daemon allows system administrators to configure their Linux systems to share file systems and directories with Windows desktops. Samba will advertise the file systems and directories via the Server Message Block (SMB) protocol. Windows desktop users will be able to mount these directories and file systems as letter drives on their systems. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If there is no need to mount directories and file systems to Windows systems, then this package can be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-6, CM-7 + + + + +Run the following command to stop smb.service + and remove samba + package: + +# systemctl stop smb.service
+# dnf remove samba +
+ + - OR - + + + - IF - + the samba + package is required as a dependency: + +Run the following commands to stop and mask the smb.service +: + +# systemctl stop smb.service
+# systemctl mask smb.service +
+ Impact: + + +There may be packages that are dependent on the samba + package. If the samba + package is removed, these dependent packages will be removed as well. Before removing the samba + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the smb.service + leaving the samba + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure ftp server services are not in use + + FTP (File Transfer Protocol) is a traditional and widely used standard tool for transferring files between a server and clients over a network, especially where no authentication is necessary (permits anonymous users to connect to a server). + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless there is a need to run the system as a FTP server, it is recommended that the package be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop vsftpd.service + and remove vsftpd + package: + +# systemctl stop vsftpd.service
+# dnf remove vsftpd +
+ + - OR - + + + - IF - + the vsftpd + package is required as a dependency: + +Run the following commands to stop and mask the vsftpd.service +: + +# systemctl stop vsftpd.service
+# systemctl mask vsftpd.service +
+ + Note: + Other ftp server packages may exist. If not required and authorized by local site policy, they should also be removed. If the package is required for a dependency, the service should be stopped and masked. + Impact: + + +There may be packages that are dependent on the vsftpd + package. If the vsftpd + package is removed, these dependent packages will be removed as well. Before removing the vsftpd + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the vsftpd.service + leaving the vsftpd + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure message access server services are not in use + + + dovecot + and cyrus-imapd + are open source IMAP and POP3 server packages for Linux based systems. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless POP3 and/or IMAP servers are to be provided by this system, it is recommended that the package be removed to reduce the potential attack surface. + + Note: + Several IMAP/POP3 servers exist and can use other service names. These should also be audited and the packages removed if not required. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop dovecot.socket +, dovecot.service +, and cyrus-imapd.service +, and remove dovecot + and cyrus-imapd + packages: + +# systemctl stop dovecot.socket dovecot.service cyrus-imapd.service
+# dnf remove dovecot cyrus-imapd +
+ + - OR - + + + - IF - + a package is installed and + is required for dependencies: + +Run the following commands to stop and mask dovecot.socket +, dovecot.service +, and cyrus-imapd.service +: + +# systemctl stop dovecot.socket dovecot.service cyrus-imapd.service
+# systemctl mask dovecot.socket dovecot.service cyrus-imapd.service +
+ Impact: + + +There may be packages that are dependent on dovecot + and cyrus-imapd + packages. If dovecot + and cyrus-imapd + packages are removed, these dependent packages will be removed as well. Before removing dovecot + and cyrus-imapd + packages, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask dovecot.socket +, dovecot.service + and cyrus-imapd.service + leaving dovecot + and cyrus-imapd + packages installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure network file system services are not in use + + The Network File System (NFS) is one of the first and most widely distributed file systems in the UNIX environment. It provides the ability for systems to mount file systems of other servers through the network. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + Many of the libvirt packages used by Enterprise Linux virtualization are dependent on the nfs-utils package. If the nfs-utils package is required as a dependency, the nfs-server service should be disabled and masked to reduce the attack surface of the system. + + + + +If the system does not require access to network shares or the ability to provide network file system services for other host's network shares, it is recommended that the nfs-utils + package be removed to reduce the attack surface of the system. + + + + NIST SP 800-53 Rev. 5: CM-6, CM-7 + + + + +Run the following command to stop nfs-server.service + and remove nfs-utils + package: + +# systemctl stop nfs-server.service
+# dnf remove nfs-utils +
+ + - OR - + + + - IF - + the nfs-utils + package is required as a dependency: + +Run the following commands to stop and mask the nfs-server.service +: + +# systemctl stop nfs-server.service
+# systemctl mask nfs-server.service +
+ Impact: + + +Many of the libvirt + packages used by Enterprise Linux virtualization are dependent on the nfs-utils + package. If the nfs-utils + package is removed, these dependent packages will be removed as well. Before removing the nfs-utils + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the nfs-server.service + leaving the nfs-utils + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure nis server services are not in use + + +The Network Information Service (NIS), formerly known as Yellow Pages, is a client-server directory service protocol used to distribute system configuration files. The NIS client ( ypbind + ) was used to bind a machine to an NIS server and receive the distributed configuration files. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + The NIS service is inherently an insecure system that has been vulnerable to DOS attacks, buffer overflows and has poor authentication for querying NIS maps. NIS generally has been replaced by such protocols as Lightweight Directory Access Protocol (LDAP). It is recommended that the service be removed. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop ypserv.service + and remove ypserv + package: + +# systemctl stop ypserv.service
+# dnf remove ypserv +
+ + - OR - + + + - IF - + the ypserv + package is required as a dependency: + +Run the following commands to stop and mask ypserv.service +: + +# systemctl stop ypserv.service
+# systemctl mask ypserv.service +
+ Impact: + + +There may be packages that are dependent on the ypserv + package. If the ypserv + package is removed, these dependent packages will be removed as well. Before removing the ypserv + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the ypserv.service + leaving the ypserv + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure print server services are not in use + + The Common Unix Print System (CUPS) provides the ability to print to both local and network printers. A system running CUPS can also accept print jobs from remote systems and print them to local printers. It also provides a web based remote administration capability. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If the system does not need to print jobs or accept print jobs from other systems, it is recommended that CUPS be removed to reduce the potential attack surface. + + + + http://www.cups.org + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop cups.socket + and cups.service +, and remove the cups + package: + +# systemctl stop cups.socket cups.service
+# dnf remove cups +
+ + - OR - + + + - IF - + the cups + package is required as a dependency: + +Run the following commands to stop and mask the cups.socket + and cups.service +: + +# systemctl stop cups.socket cups.service
+# systemctl mask cups.socket cups.service +
+ Impact: + + +Removing the cups package, or disabling cups.socket + and/or cups.service + will prevent printing from the system, a common task for workstation systems. + +There may be packages that are dependent on the cups + package. If the cups + package is removed, these dependent packages will be removed as well. Before removing the cups + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask cups.socket + and cups.service + leaving the cups + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure rpcbind services are not in use + + +The rpcbind + utility maps RPC services to the ports on which they listen. RPC processes notify rpcbind + when they start, registering the ports they are listening on and the RPC program numbers they expect to serve. The client system then contacts rpcbind + on the server with a particular RPC program number. The rpcbind.service + redirects the client to the proper port number so it can communicate with the requested service. + Portmapper is an RPC service, which always listens on tcp and udp 111, and is used to map other RPC services (such as nfs, nlockmgr, quotad, mountd, etc.) to their corresponding port number on the server. When a remote host makes an RPC call to that server, it first consults with portmap to determine where the RPC server is listening. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +A small request (~82 bytes via UDP) sent to the Portmapper generates a large response (7x to 28x amplification), which makes it a suitable tool for DDoS attacks. If rpcbind + is not required, it is recommended to remove rpcbind + package to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-6, CM-7 + + + + +Run the following commands to stop rpcbind.socket + and rpcbind.service +, and remove the rpcbind + package: + +# systemctl stop rpcbind.socket rpcbind.service
+# dnf remove rpcbind +
+ + - OR - + + + - IF - + the rpcbind + package is required as a dependency: + +Run the following commands to stop and mask the rpcbind.socket + and rpcbind.service +: + +# systemctl stop rpcbind.socket rpcbind.service
+# systemctl mask rpcbind.socket rpcbind.service +
+ Impact: + + +Many of the libvirt packages used by Enterprise Linux virtualization, and the nfs-utils + package used for The Network File System (NFS), are dependent on the rpcbind + package. If the rpcbind + package is removed, these dependent packages will be removed as well. Before removing the rpcbind + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the rpcbind.socket + and rpcbind.service + leaving the rpcbind + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure rsync services are not in use + + +The rsyncd.service + can be used to synchronize files between systems over network links. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Unless required, the rsync-daemon + package should be removed to reduce the potential attack surface. + +The rsyncd.service + presents a security risk as it uses unencrypted protocols for communication. + + + + NIST SP 800-53 Rev. 5: CM-6, CM-7 + + + + +Run the following commands to stop rsyncd.socket + and rsyncd.service +, and remove the rsync-daemon + package: + +# systemctl stop rsyncd.socket rsyncd.service
+# dnf remove rsync-daemon +
+ + - OR - + + + - IF - + the rsync-daemon + package is required as a dependency: + +Run the following commands to stop and mask the rsyncd.socket + and rsyncd.service +: + +# systemctl stop rsyncd.socket rsyncd.service
+# systemctl mask rsyncd.socket rsyncd.service +
+ Impact: + + +There may be packages that are dependent on the rsync-daemon + package. If the rsync-daemon + package is removed, these dependent packages will be removed as well. Before removing the rsync-daemon + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the rsyncd.socket + and rsyncd.service + leaving the rsync-daemon + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure snmp services are not in use + + Simple Network Management Protocol (SNMP) is a widely used protocol for monitoring the health and welfare of network equipment, computer equipment and devices like UPSs. + Net-SNMP is a suite of applications used to implement SNMPv1 (RFC 1157), SNMPv2 (RFCs 1901-1908), and SNMPv3 (RFCs 3411-3418) using both IPv4 and IPv6. + Support for SNMPv2 classic (a.k.a. "SNMPv2 historic" - RFCs 1441-1452) was dropped with the 4.0 release of the UCD-snmp package. + The Simple Network Management Protocol (SNMP) server is used to listen for SNMP commands from an SNMP management system, execute the commands or collect the information and then send results back to the requesting system. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +The SNMP server can communicate using SNMPv1 +, which transmits data in the clear and does not require authentication to execute commands. SNMPv3 + replaces the simple/clear text password sharing used in SNMPv2 + with more securely encoded parameters. If the the SNMP service is not required, the net-snmp + package should be removed to reduce the attack surface of the system. + + Note: + If SNMP is required: + + +The server should be configured for SNMP v3 + only. User Authentication + and Message Encryption + should be configured. + +If SNMP v2 + is absolutely + necessary, modify the community strings' values. + + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop snmpd.service + and remove net-snmp + package: + +# systemctl stop snmpd.service
+# dnf remove net-snmp +
+ + - OR - + If the package is required for dependencies: + +Run the following commands to stop and mask the snmpd.service +: + +# systemctl stop snmpd.service
+# systemctl mask snmpd.service +
+ Impact: + + +There may be packages that are dependent on the net-snmp + package. If the net-snmp + package is removed, these packages will be removed as well. + +Before removing the net-snmp + package, review any dependent packages to determine if they are required on the system. If a dependent package is required, stop and mask the snmpd.service + leaving the net-snmp + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure telnet server services are not in use + + +The telnet-server + package contains the telnet + daemon, which accepts connections from users from other systems via the telnet + protocol. + + + + + + + Devices + Protect + + + + + + Applications + Respond + + + + Devices + Protect + + + + + + +The telnet + protocol is insecure and unencrypted. The use of an unencrypted transmission medium could allow a user with access to sniff network traffic the ability to steal credentials. The ssh + package provides an encrypted session and stronger security. + + + + + NIST SP 800-53 Rev. 5: CM-7, CM-11 + + + + +Run the following commands to stop telnet.socket + and remove the telnet-server + package: + +# systemctl stop telnet.socket
+# dnf remove telnet-server +
+ + - OR - + + + - IF - + a package is installed and + is required for dependencies: + +Run the following commands to stop and mask telnet.socket +: + +# systemctl stop telnet.socket
+# systemctl mask telnet.socket +
+ Impact: + + +There may be packages that are dependent on the telnet-server + package. If the telnet-server + package is removed, these dependent packages will be removed as well. Before removing the telnet-server + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the telnet.socket + leaving the telnet-server + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure tftp server services are not in use + + Trivial File Transfer Protocol (TFTP) is a simple protocol for exchanging files between two TCP/IP machines. TFTP servers allow connections from a TFTP Client for sending and receiving files. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless there is a need to run the system as a TFTP server, it is recommended that the package be removed to reduce the potential attack surface. + TFTP does not have built-in encryption, access control or authentication. This makes it very easy for an attacker to exploit TFTP to gain access to files + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop tftp.socket + and tftp.service +, and remove the tftp-server + package: + +# systemctl stop tftp.socket tftp.service
+# dnf remove tftp-server +
+ + - OR - + + + - IF - + the tftp-server + package is required as a dependency: + +Run the following commands to stop and mask tftp.socket + and tftp.service +: + +# systemctl stop tftp.socket tftp.service
+# systemctl mask tftp.socket tftp.service +
+ Impact: + + TFTP is often used to provide files for network booting such as for PXE based installation of servers. + +There may be packages that are dependent on the tftp-server + package. If the tftp-server + package is removed, these dependent packages will be removed as well. Before removing the tftp-server + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the tftp.socket + and tftp.service + leaving the tftp-server + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure web proxy server services are not in use + + Squid is a standard proxy server used in many distributions and environments. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + Several HTTP proxy servers exist. These and other services should be checked. + + + + Unless a system is specifically set up to act as a proxy server, it is recommended that the squid package be removed to reduce the potential attack surface. + + Note: + Several HTTP proxy servers exist. These should be checked and removed unless required. + + + + NIST SP 800-53 Rev. 5: CM-6, CM-7 + + + + +Run the following commands to stop squid.service + and remove the squid + package: + +# systemctl stop squid.service
+# dnf remove squid +
+ + - OR - + If the squid + package is required as a dependency: + +Run the following commands to stop and mask the squid.service +: + +# systemctl stop squid.service
+# systemctl mask squid.service +
+ Impact: + + +There may be packages that are dependent on the squid + package. If the squid + package is removed, these dependent packages will be removed as well. Before removing the squid + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the squid.service + leaving the squid + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure web server services are not in use + + Web servers provide the ability to host web site content. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless there is a local site approved requirement to run a web server service on the system, web server packages should be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop httpd.socket +, httpd.service +, and nginx.service +, and remove httpd + and nginx + packages: + +# systemctl stop httpd.socket httpd.service nginx.service
+# dnf remove httpd nginx +
+ + - OR - + + + - IF - + a package is installed and + is required for dependencies: + +Run the following commands to stop and mask httpd.socket +, httpd.service +, and nginx.service +: + +# systemctl stop httpd.socket httpd.service nginx.service
+# systemctl mask httpd.socket httpd.service nginx.service +
+ + Note: + Other web server packages may exist. If not required and authorized by local site policy, they should also be removed. If the package is required for a dependency, the service and socket should be stopped and masked. + Impact: + + Removal of web server packages will remove that ability for the server to host web services. + + - IF - + the web server package is required for a dependency, any related service or socket should be stopped and masked. + + Note: + If the remediation steps to mask a service are followed and that package is not installed on the system, the service and/or socket will still be masked. If the package is installed due to an approved requirement to host a web server, the associated service and/or socket would need to be unmasked before it could be enabled and/or started. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure xinetd services are not in use + + +The eXtended InterNET Daemon ( xinetd +) is an open source super daemon that replaced the original inetd + daemon. The xinetd + daemon listens for well known services and dispatches the appropriate daemon to properly respond to service requests. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +If there are no xinetd + services required, it is recommended that the package be removed to reduce the attack surface are of the system. + + Note: + If an xinetd + service or services are required, ensure that any xinetd + service not required is stopped and masked + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop xinetd.service +, and remove the xinetd + package: + +# systemctl stop xinetd.service
+# dnf remove xinetd +
+ + - OR - + + + - IF - + the xinetd + package is required as a dependency: + +Run the following commands to stop and mask the xinetd.service +: + +# systemctl stop xinetd.service
+# systemctl mask xinetd.service +
+ Impact: + + +There may be packages that are dependent on the xinetd + package. If the xinetd + package is removed, these dependent packages will be removed as well. Before removing the xinetd + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the avahi-daemon.socket and avahi-daemon.service leaving the avahi package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure X window server services are not in use + + The X Window System provides a Graphical User Interface (GUI) where users can have multiple windows in which to run programs and various add on. The X Windows system is typically used on workstations where users login, but not on servers where users typically do not login. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless your organization specifically requires graphical login access via X Windows, remove it to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-11 + + + + + - IF - + a Graphical Desktop Manager or X-Windows server is not required and approved by local site policy: + Run the following command to remove the X Windows Server packages: + # dnf remove xorg-x11-server-common + + Impact: + + +If a Graphical Desktop Manager (GDM) is in use on the system, there may be a dependency on the xorg-x11-server-common + package. If the GDM is required and approved by local site policy, the package should not + be removed. + Many Linux systems run applications which require a Java runtime. Some Linux Java packages have a dependency on specific X Windows xorg-x11-fonts. One workaround to avoid this dependency is to use the "headless" Java packages for your specific Java runtime. + + + + + + + + + + + + + + + Ensure mail transfer agents are configured for local-only mode + + Mail Transfer Agents (MTA), such as sendmail and Postfix, are used to listen for incoming mail and transfer the messages to the appropriate user or mail server. If the system is not intended to be a mail server, it is recommended that the MTA be configured to only process local mail. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + The software for all Mail Transfer Agents is complex and most have a long history of security issues. While it is important to ensure that the system can process local mail messages, it is not necessary to have the MTA's daemon listening on a port unless the server is intended to be a mail server that receives and processes mail from other systems. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Edit /etc/postfix/main.cf + and add the following line to the RECEIVING MAIL section. If the line already exists, change it to look like the line below: + inet_interfaces = loopback-only + + +Run the following command to restart postfix +: + # systemctl restart postfix + + + Note: + + + This remediation is designed around the postfix mail server. + Depending on your environment you may have an alternative MTA installed such as sendmail. If this is the case consult the documentation for your installed MTA to configure the recommended state. + + + + + + + + + + + + + Ensure only approved services are listening on a network interface + + A network port is identified by its number, the associated IP address, and the type of the communication protocol such as TCP or UDP. + A listening port is a network port on which an application or process listens on, acting as a communication endpoint. + Each listening port can be open or closed (filtered) using a firewall. In general terms, an open port is a network port that accepts incoming packets from remote locations. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Services listening on the system pose a potential risk as an attack vector. These services should be reviewed, and if not required, the service should be stopped, and the package containing the service should be removed. If required packages have a dependency, the service should be stopped and masked to reduce the attack surface of the system. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + Run the following commands to stop the service and remove the package containing the service: + +# systemctl stop <service_name>.socket <service_name>.service
+# dnf remove <package_name> +
+ + - OR - + If required packages have a dependency: + Run the following commands to stop and mask the service and socket: + +# systemctl stop <service_name>.socket <service_name>.service
+# systemctl mask <service_name>.socket <service_name>.service +
+ + Note: + replace <service_name> + with the appropriate service name. + Impact: + + There may be packages that are dependent on the service's package. If the service's package is removed, these dependent packages will be removed as well. Before removing the service's package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask the <service_name>.socket + and <service_name>.service + leaving the service's package installed. + +
+
+
+
+
+ + Configure Client Services + + A number of insecure services exist. While disabling the servers prevents a local attack against these services, it is advised to remove their clients unless they are required. + + Note: + This should not be considered a comprehensive list of insecure service clients. You may wish to consider additions to those listed here for your environment. + + + Ensure ftp client is not installed + + FTP (File Transfer Protocol) is a traditional and widely used standard tool for transferring files between a server and clients over a network, especially where no authentication is necessary (permits anonymous users to connect to a server). + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + FTP does not protect the confidentiality of data or authentication credentials. It is recommended SFTP be used if file transfer is required. Unless there is a need to run the system as a FTP server (for example, to allow anonymous downloads), it is recommended that the package be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following command to remove ftp +: + # dnf remove ftp + + + + + + + + + + + + Ensure ldap client is not installed + + The Lightweight Directory Access Protocol (LDAP) was introduced as a replacement for NIS/YP. It is a service that provides a method for looking up information from a central database. + + + + + + + Devices + Protect + + + + + + Applications + Respond + + + + + + If the system will not need to act as an LDAP client, it is recommended that the software be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following command to remove the openldap-clients + package: + # dnf remove openldap-clients + + Impact: + + Removing the LDAP client will prevent or inhibit using LDAP for authentication in your environment. + + + + + + + + + + + + Ensure nis client is not installed + + +The Network Information Service (NIS), formerly known as Yellow Pages, is a client-server directory service protocol used to distribute system configuration files. The NIS client ( ypbind + ) was used to bind a machine to an NIS server and receive the distributed configuration files. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + The NIS service is inherently an insecure system that has been vulnerable to DOS attacks, buffer overflows and has poor authentication for querying NIS maps. NIS generally has been replaced by such protocols as Lightweight Directory Access Protocol (LDAP). It is recommended that the service be removed. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + Run the following command to remove the ypbind package: + # dnf remove ypbind + + Impact: + + Many insecure service clients are used as troubleshooting tools and in testing environments. Uninstalling them can inhibit capability to test and troubleshoot. If they are required it is advisable to remove the clients after use to prevent accidental or intentional misuse. + + + + + + + + + + + + Ensure telnet client is not installed + + +The telnet + package contains the telnet + client, which allows users to start connections to other systems via the telnet protocol. + + + + + + + Devices + Protect + + + + + + Applications + Respond + + + + + + +The telnet + protocol is insecure and unencrypted. The use of an unencrypted transmission medium could allow an unauthorized user to steal credentials. The ssh + package provides an encrypted session and stronger security and is included in most Linux distributions. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following command to remove the telnet + package: + # dnf remove telnet + + Impact: + + Many insecure service clients are used as troubleshooting tools and in testing environments. Uninstalling them can inhibit capability to test and troubleshoot. If they are required it is advisable to remove the clients after use to prevent accidental or intentional misuse. + + + + + + + + + + + + Ensure tftp client is not installed + + Trivial File Transfer Protocol (TFTP) is a simple protocol for exchanging files between two TCP/IP machines. TFTP servers allow connections from a TFTP Client for sending and receiving files. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + TFTP does not have built-in encryption, access control or authentication. This makes it very easy for an attacker to exploit TFTP to gain access to files + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following command to remove tftp +: + # dnf remove tftp + + + + + + + + + + + + + Configure Time Synchronization + + +It is recommended that systems be configured to synchronize their time using a service such as chrony +. + Virtual systems may be configured to receive their time synchronization from their host system. + The host system must be configured to synchronize its time from an authoritative source to be considered compliant with this section. + Any "physical" clock present on a system should be synchronized from an authoritative time source. + + Only one time synchronization method should be in use on the system + + + Notes: + Only the section related to the time synchronization method in use on the system should be followed, all other time synchronization recommendations should be skipped + + + Ensure time synchronization is in use + + System time should be synchronized between all systems in an environment. This is typically done by establishing an authoritative time server or set of servers and having all systems synchronize their clocks to them. + + Note: + If another method for time synchronization is being used, this section may be skipped. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +On systems where host based time synchronization is not available, verify that chrony + is installed. + On systems where host based time synchronization is available consult your documentation and verify that host based synchronization is in use. + + + + Time synchronization is important to support time sensitive security mechanisms like Kerberos and also ensures log files have consistent time records across the enterprise, which aids in forensic investigations. + + + + NIST SP 800-53 Rev. 5: AU-3, AU-12 + + + + +Run the following command to install chrony +: + # dnf install chrony + + + + + + + + + + + + Ensure chrony is configured + + + chrony + is a daemon which implements the Network Time Protocol (NTP) and is designed to synchronize system clocks across a variety of systems and use a source that is highly accurate. More information on chrony + can be found at http://chrony.tuxfamily.org/ +. chrony + can be configured to be a client and/or a server. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +On systems where host based time synchronization is not available, verify that chrony + is installed. + On systems where host based time synchronization is available consult your documentation and verify that host based synchronization is in use. + + + + +If chrony + is in use on the system proper configuration is vital to ensuring time synchronization is working properly. + + + + NIST SP 800-53 Rev. 5: AU-3, AU-12 + + + + +Add or edit server or pool lines to /etc/chrony.conf + or a file in the /etc/chrony.d + directory as appropriate: + + Example: + + server <remote-server> + + + + + + + + + + + + + + + + + + + + Ensure chrony is not run as the root user + + +The file /etc/sysconfig/chronyd + allows configuration of options for chrony + to include the user chrony + is run as. By default this is set to the user chrony + + + + Services should not be set to run as the root user + + + + + +Edit the file /etc/sysconfig/chronyd + and add or modify the following line to remove -u root +: + + Example: + + OPTIONS="-F 2" + + +Run the following command to reload the chronyd.service + configuration: + # systemctl reload-or-restart chronyd.service + + + + + + + + + + + + + Job Schedulers + + A job scheduler is used to execute jobs, commands, or shell scripts, at fixed times, dates, or intervals + + + Configure cron + + + cron + is a time based job scheduler + + Notes: + + + +Other methods, such as systemd timers +, exist for scheduling jobs. If another method is used, cron + should be removed, and the alternate method should be secured in accordance with local site policy + + - IF - + cron is not installed on the system, this section can be skipped + + + + Ensure cron daemon is enabled and active + + +The cron + daemon is used to execute batch jobs on the system. + + + +While there may not be user jobs that need to be run on the system, the system does have maintenance jobs that may include security monitoring that have to run, and cron + is used to execute them. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + + - IF - + cron is installed on the system: + +Run the following commands to unmask, enable, and start cron +: + +# systemctl unmask "$(systemctl list-unit-files | awk '$1~/^crond?\.service/{print $1}')"
+# systemctl --now enable "$(systemctl list-unit-files | awk '$1~/^crond?\.service/{print $1}')" +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/crontab are configured + + +The /etc/crontab + file is used by cron + to control its own jobs. The commands in this item make sure that root is the user and group owner of the file and that only the owner can access the file. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + This file contains information on what system jobs are run by cron. Write access to these files could provide unprivileged users with the ability to elevate their privileges. Read access to these files could provide users with the ability to gain insight on system jobs that run on the system and could provide them a way to gain unauthorized privileged access. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + cron is installed on the system: + +Run the following commands to set ownership and permissions on /etc/crontab +: + +# chown root:root /etc/crontab
+# chmod og-rwx /etc/crontab +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/cron.hourly are configured + + +This directory contains system cron + jobs that need to run on an hourly basis. The files in this directory cannot be manipulated by the crontab + command, but are instead edited by system administrators using a text editor. The commands below restrict read/write and search access to user and group root, preventing regular users from accessing this directory. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Granting write access to this directory for non-privileged users could provide them the means for gaining unauthorized elevated privileges. Granting read access to this directory could give an unprivileged user insight in how to gain elevated privileges or circumvent auditing controls. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + - IF - + cron is installed on the system: + +Run the following commands to set ownership and permissions on the /etc/cron.hourly + directory: + +# chown root:root /etc/cron.hourly/
+# chmod og-rwx /etc/cron.hourly/ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/cron.daily are configured + + +The /etc/cron.daily + directory contains system cron jobs that need to run on a daily basis. The files in this directory cannot be manipulated by the crontab + command, but are instead edited by system administrators using a text editor. The commands below restrict read/write and search access to user and group root, preventing regular users from accessing this directory. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Granting write access to this directory for non-privileged users could provide them the means for gaining unauthorized elevated privileges. Granting read access to this directory could give an unprivileged user insight in how to gain elevated privileges or circumvent auditing controls. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + - IF - + cron is installed on the system: + +Run the following commands to set ownership and permissions on the /etc/cron.daily + directory: + +# chown root:root /etc/cron.daily/
+# chmod og-rwx /etc/cron.daily/ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/cron.weekly are configured + + +The /etc/cron.weekly + directory contains system cron jobs that need to run on a weekly basis. The files in this directory cannot be manipulated by the crontab + command, but are instead edited by system administrators using a text editor. The commands below restrict read/write and search access to user and group root, preventing regular users from accessing this directory. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Granting write access to this directory for non-privileged users could provide them the means for gaining unauthorized elevated privileges. Granting read access to this directory could give an unprivileged user insight in how to gain elevated privileges or circumvent auditing controls. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + - IF - + cron is installed on the system: + +Run the following commands to set ownership and permissions on the /etc/cron.weekly + directory: + +# chown root:root /etc/cron.weekly/
+# chmod og-rwx /etc/cron.weekly/ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/cron.monthly are configured + + +The /etc/cron.monthly + directory contains system cron jobs that need to run on a monthly basis. The files in this directory cannot be manipulated by the crontab + command, but are instead edited by system administrators using a text editor. The commands below restrict read/write and search access to user and group root, preventing regular users from accessing this directory. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Granting write access to this directory for non-privileged users could provide them the means for gaining unauthorized elevated privileges. Granting read access to this directory could give an unprivileged user insight in how to gain elevated privileges or circumvent auditing controls. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + - IF - + cron is installed on the system: + +Run the following commands to set ownership and permissions on the /etc/cron.monthly + directory: + +# chown root:root /etc/cron.monthly/
+# chmod og-rwx /etc/cron.monthly/ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/cron.d are configured + + +The /etc/cron.d + directory contains system cron + jobs that need to run in a similar manner to the hourly, daily weekly and monthly jobs from /etc/crontab +, but require more granular control as to when they run. The files in this directory cannot be manipulated by the crontab + command, but are instead edited by system administrators using a text editor. The commands below restrict read/write and search access to user and group root, preventing regular users from accessing this directory. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Granting write access to this directory for non-privileged users could provide them the means for gaining unauthorized elevated privileges. Granting read access to this directory could give an unprivileged user insight in how to gain elevated privileges or circumvent auditing controls. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + - IF - + cron is installed on the system: + +Run the following commands to set ownership and permissions on the /etc/cron.d + directory: + +# chown root:root /etc/cron.d/
+# chmod og-rwx /etc/cron.d/ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure crontab is restricted to authorized users + + + crontab + is the program used to install, deinstall, or list the tables used to drive the cron daemon. Each user can have their own crontab, and though these are files in /var/spool/cron/crontabs +, they are not intended to be edited directly. + +If the /etc/cron.allow + file exists, then you must be listed (one user per line) therein in order to be allowed to use this command. If the /etc/cron.allow + file does not exist but the /etc/cron.deny + file does exist, then you must not be listed in the /etc/cron.deny + file in order to use this command. + If neither of these files exists, then depending on site-dependent configuration parameters, only the super user will be allowed to use this command, or all users will be able to use this command. + +If both files exist then /etc/cron.allow + takes precedence. Which means that /etc/cron.deny + is not considered and your user must be listed in /etc/cron.allow + in order to be able to use the crontab. + Regardless of the existence of any of these files, the root administrative user is always allowed to setup a crontab. + +The files /etc/cron.allow + and /etc/cron.deny +, if they exist, must be either world-readable, or readable by group crontab +. If they are not, then cron will deny access to all users until the permissions are fixed. + +There is one file for each user's crontab under the /var/spool/cron/crontabs + directory. Users are not allowed to edit the files under that directory directly to ensure that only users allowed by the system to run periodic tasks can add them, and only syntactically correct crontabs will be written there. This is enforced by having the directory writable only by the crontab + group and configuring crontab command with the setgid bid set for that specific group. + + Note: + + + +Even though a given user is not listed in cron.allow +, cron jobs can still be run as that user + +The files /etc/cron.allow + and /etc/cron.deny +, if they exist, only controls administrative access to the crontab command for scheduling and modifying cron jobs + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +On many systems, only the system administrator is authorized to schedule cron + jobs. Using the cron.allow + file to control who can run cron + jobs enforces this policy. It is easier to manage an allow list than a deny list. In a deny list, you could potentially add a user ID to the system and forget to add it to the deny files. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + - IF - + cron is installed on the system: + Run the following script to: + + +Create /etc/cron.allow + if it doesn't exist + +Change owner to user root + + +Change group owner to group root + + +Change mode to 640 + or more restrictive + + +#!/usr/bin/env bash
+
+{
+ [ ! -e "/etc/cron.allow" ] && touch /etc/cron.allow
+ chown root:root /etc/cron.allow
+ chmod u-x,g-wx,o-rwx /etc/cron.allow
+} +
+ + - IF - + /etc/cron.deny + exists, run the following commands to: + + +Change owner to user root + + +Change group owner to group root + + +Change mode to 640 + or more restrictive + + +# [ -e "/etc/cron.deny" ] && chown root:root /etc/cron.deny
+# [ -e "/etc/cron.deny" ] && chmod u-x,g-wx,o-rwx /etc/cron.deny +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + Configure at + + + at + is a command-line utility used to schedule a job for later execution + + Note: + if at + is not installed on the system, this section can be skipped + + + Ensure at is restricted to authorized users + + + at + allows fairly complex time specifications, extending the POSIX.2 standard. It accepts times of the form HH:MM to run a job at a specific time of day. (If that time is already past, the next day is assumed.) You may also specify midnight, noon, or teatime (4pm) and you can have a time-of-day suffixed with AM or PM for running in the morning or the evening. You can also say what day the job will be run, by giving a date in the form month-name day with an optional year, or giving a date of the form MMDD[CC]YY, MM/DD/[CC]YY, DD.MM.[CC]YY or [CC]YY-MM-DD. The specification of a date must follow the specification of the time of day. You can also give times like now + count time-units, where the time-units can be minutes, hours, days, or weeks and you can tell at to run the job today by suffixing the time with today and to run the job tomorrow by suffixing the time with tomorrow. + +The /etc/at.allow + and /etc/at.deny + files determine which user can submit commands for later execution via at or batch. The format of the files is a list of usernames, one on each line. Whitespace is not permitted. If the file /etc/at.allow + exists, only usernames mentioned in it are allowed to use at. If /etc/at.allow + does not exist, /etc/at.deny + is checked, every username not mentioned in it is then allowed to use at. An empty /etc/at.deny + means that every user may use at. If neither file exists, only the superuser is allowed to use at. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +On many systems, only the system administrator is authorized to schedule at + jobs. Using the at.allow + file to control who can run at + jobs enforces this policy. It is easier to manage an allow list than a deny list. In a deny list, you could potentially add a user ID to the system and forget to add it to the deny files. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + - IF - + at is installed on the system: + Run the following script to: + + + /etc/at.allow +: + + Create the file if it doesn't exist + +Change owner or user root + + +If group daemon + exists, change to group daemon +, else change group to root + + +Change mode to 640 + or more restrictive + + + + - IF - + /etc/at.deny + exists: + + +Change owner or user root + + +If group daemon + exists, change to group daemon +, else change group to root + + +Change mode to 640 + or more restrictive + + + + +#!/usr/bin/env bash
+
+{
+ grep -Pq -- '^daemon\b' /etc/group && l_group="daemon" || l_group="root"
+ [ ! -e "/etc/at.allow" ] && touch /etc/at.allow
+ chown root:"$l_group" /etc/at.allow
+ chmod u-x,g-wx,o-rwx /etc/at.allow
+ [ -e "/etc/at.deny" ] && chown root:"$l_group" /etc/at.deny
+ [ -e "/etc/at.deny" ] && chmod u-x,g-wx,o-rwx /etc/at.deny
+} +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + Network + + This section provides guidance on for securing the network configuration of the system + + + Configure Network Devices + + To reduce the attack surface of a system, unused devices should be disabled. + + Note: + This should not be considered a comprehensive list, you may wish to consider additions to those listed here for your environment. + + + Ensure IPv6 status is identified + + Internet Protocol Version 6 (IPv6) is the most recent version of Internet Protocol (IP). It's designed to supply IP addressing and additional security to support the predicted growth of connected devices. IPv6 is based on 128-bit addressing and can support 340 undecillion, which is 340,282,366,920,938,463,463,374,607,431,768,211,456 unique addresses. + Features of IPv6 + + Hierarchical addressing and routing infrastructure + Statefull and Stateless configuration + Support for quality of service (QoS) + An ideal protocol for neighboring node interaction + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + Having more addresses has grown in importance with the expansion of smart devices and connectivity. IPv6 provides more than enough globally unique IP addresses for every networked device currently on the planet, helping ensure providers can keep pace with the expected proliferation of IP-based devices. + + + + IETF RFC 4038 recommends that applications are built with an assumption of dual stack. It is recommended that IPv6 be enabled and configured in accordance with Benchmark recommendations. + + - IF - + dual stack and IPv6 are not used in your environment, IPv6 may be disabled to reduce the attack surface of the system, and recommendations pertaining to IPv6 can be skipped. + + Note: + It is recommended that IPv6 be enabled and configured unless this is against local site policy + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + Enable or disable IPv6 in accordance with system requirements and local site policy + Impact: + + IETF RFC 4038 recommends that applications are built with an assumption of dual stack. + When enabled, IPv6 will require additional configuration to reduce risk to the system. + + + + + + + + + + + + + Ensure wireless interfaces are disabled + + Wireless networking is used when wired networks are unavailable. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + Devices + Protect + + + + + + + - IF - + wireless is not to be used, wireless devices can be disabled to reduce the potential attack surface. + + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + Run the following script to disable any wireless interfaces: + +#!/usr/bin/env bash
+
+{
+ module_fix()
+ {
+ if ! modprobe -n -v "$l_mname" | grep -P -- '^\h*install \/bin\/(true|false)'; then
+ echo -e " - setting module: \"$l_mname\" to be un-loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mname".conf
+ fi
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e " - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ if ! grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
+ echo -e " - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mname".conf
+ fi
+ }
+ if [ -n "$(find /sys/class/net/*/ -type d -name wireless)" ]; then
+ l_dname=$(for driverdir in $(find /sys/class/net/*/ -type d -name wireless | xargs -0 dirname); do basename "$(readlink -f "$driverdir"/device/driver/module)";done | sort -u)
+ for l_mname in $l_dname; do
+ module_fix
+ done
+ fi
+} +
+ Impact: + + Many if not all laptop workstations and some desktop workstations will connect via wireless requiring these interfaces be enabled. + +
+
+
+ + + + + + +
+ + Ensure bluetooth services are not in use + + Bluetooth is a short-range wireless technology standard that is used for exchanging data between devices over short distances. It employs UHF radio waves in the ISM bands, from 2.402 GHz to 2.48 GHz. It is mainly used as an alternative to wire connections. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +An attacker may be able to find a way to access or corrupt your data. One example of this type of activity is bluesnarfing +, which refers to attackers using a Bluetooth connection to steal information off of your Bluetooth device. Also, viruses or other malicious code can take advantage of Bluetooth technology to infect other devices. If you are infected, your data may be corrupted, compromised, stolen, or lost. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop bluetooth.service +, and remove the bluez + package: + +# systemctl stop bluetooth.service
+# dnf remove bluez +
+ + - OR - + + + - IF - + the bluez + package is required as a dependency: + +Run the following commands to stop and mask bluetooth.service +: + +# systemctl stop bluetooth.service
+# systemctl mask bluetooth.service +
+ + Note: + A reboot may be required + Impact: + + Many personal electronic devices (PEDs) use Bluetooth technology. For example, you may be able to operate your computer with a wireless keyboard. Disabling Bluetooth will prevent these devices from connecting to the system. + +There may be packages that are dependent on the bluez + package. If the bluez + package is removed, these dependent packages will be removed as well. Before removing the bluez + package, review any dependent packages to determine if they are required on the system. + + - IF - + a dependent package is required: stop and mask bluetooth.service + leaving the bluez + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+
+ + Configure Network Kernel Modules + + The Linux kernel modules support several network protocols that are not commonly used. If these protocols are not needed, it is recommended that they be disabled in the kernel. + + Note: + This should not be considered a comprehensive list of uncommon network protocols, you may wish to consider additions to those listed here for your environment. + + + Ensure dccp kernel module is not available + + The Datagram Congestion Control Protocol (DCCP) is a transport layer protocol that supports streaming media and telephony. DCCP provides a way to gain access to congestion control, without having to do it at the application layer, but does not provide in-sequence delivery. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + + - IF - + the protocol is not required, it is recommended that the drivers not be installed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: SI-4, CM-7 + + + + +Run the following script to unload and disable the dccp + module: + + - IF - + the dccp + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install dccp /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist dccp + in the /etc/modprobe.d/ + directory + +Run modprobe -r dccp 2>/dev/null; rmmod dccp 2>/dev/null + to remove dccp + from the kernel + + + - IF - + the dccp + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="dccp" # set module name
+ l_mod_type="net" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure tipc kernel module is not available + + The Transparent Inter-Process Communication (TIPC) protocol is designed to provide communication between cluster nodes. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + + - IF - + the protocol is not being used, it is recommended that kernel module not be loaded, disabling the service to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: SI-4, CM-7 + + + + +Run the following script to unload and disable the tipc + module: + + - IF - + the tipc + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install tipc /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist tipc + in the /etc/modprobe.d/ + directory + +Run modprobe -r tipc 2>/dev/null; rmmod tipc 2>/dev/null + to remove tipc + from the kernel + + + - IF - + the tipc + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="tipc" # set module name
+ l_mod_type="net" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure rds kernel module is not available + + The Reliable Datagram Sockets (RDS) protocol is a transport layer protocol designed to provide low-latency, high-bandwidth communications between cluster nodes. It was developed by the Oracle Corporation. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + + - IF - + the protocol is not being used, it is recommended that kernel module not be loaded, disabling the service to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: SI-4, CM-7 + + + + +Run the following script to unload and disable the rds + module: + + - IF - + the rds + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install rds /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist rds + in the /etc/modprobe.d/ + directory + +Run modprobe -r rds 2>/dev/null; rmmod rds 2>/dev/null + to remove rds + from the kernel + + + - IF - + the rds + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="rds" # set module name
+ l_mod_type="net" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure sctp kernel module is not available + + The Stream Control Transmission Protocol (SCTP) is a transport layer protocol used to support message oriented communication, with several streams of messages in one connection. It serves a similar function as TCP and UDP, incorporating features of both. It is message-oriented like UDP, and ensures reliable in-sequence transport of messages with congestion control like TCP. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + + - IF - + the protocol is not being used, it is recommended that kernel module not be loaded, disabling the service to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: SI-4, CM-7 + + + + +Run the following script to unload and disable the sctp + module: + + - IF - + the sctp + kernel module is available in ANY installed kernel: + + +Create a file ending in .conf + with install sctp /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist sctp + in the /etc/modprobe.d/ + directory + +Run modprobe -r sctp 2>/dev/null; rmmod sctp 2>/dev/null + to remove sctp + from the kernel + + + - IF - + the sctp + kernel module is not available on the system, or pre-compiled into the kernel, no remediation is necessary + +#!/usr/bin/env bash
+
+{
+ unset a_output2; l_output3="" l_dl="" # unset arrays and clear variables
+ l_mod_name="sctp" # set module name
+ l_mod_type="net" # set module type
+ l_mod_path="$(readlink -f /lib/modules/**/kernel/$l_mod_type | sort -u)"
+ f_module_fix()
+ {
+ l_dl="y" # Set to ignore duplicate checks
+ a_showconfig=() # Create array with modprobe output
+ while IFS= read -r l_showconfig; do
+ a_showconfig+=("$l_showconfig")
+ done < <(modprobe --showconfig | grep -P -- '\b(install|blacklist)\h+'"${l_mod_name//-/_}"'\b')
+ if lsmod | grep "$l_mod_name" &> /dev/null; then # Check if the module is currently loaded
+ a_output2+=(" - unloading kernel module: \"$l_mod_name\"")
+ modprobe -r "$l_mod_name" 2>/dev/null; rmmod "$l_mod_name" 2>/dev/null
+ fi
+ if ! grep -Pq -- '\binstall\h+'"${l_mod_name//-/_}"'\h+\/bin\/(true|false)\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - setting kernel module: \"$l_mod_name\" to \"/bin/false\"")
+ printf '%s\n' "install $l_mod_name /bin/false" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ if ! grep -Pq -- '\bblacklist\h+'"${l_mod_name//-/_}"'\b' <<< "${a_showconfig[*]}"; then
+ a_output2+=(" - denylisting kernel module: \"$l_mod_name\"")
+ printf '%s\n' "blacklist $l_mod_name" >> /etc/modprobe.d/"$l_mod_name".conf
+ fi
+ }
+ for l_mod_base_directory in $l_mod_path; do # Check if the module exists on the system
+ if [ -d "$l_mod_base_directory/${l_mod_name/-/\/}" ] && [ -n "$(ls -A $l_mod_base_directory/${l_mod_name/-/\/})" ]; then
+ l_output3="$l_output3\n - \"$l_mod_base_directory\""
+ [[ "$l_mod_name" =~ overlay ]] && l_mod_name="${l_mod_name::-2}"
+ [ "$l_dl" != "y" ] && f_module_fix
+ else
+ echo -e " - kernel module: \"$l_mod_name\" doesn't exist in \"$l_mod_base_directory\""
+ fi
+ done
+ [ -n "$l_output3" ] && echo -e "\n\n -- INFO --\n - module: \"$l_mod_name\" exists in:$l_output3"
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "${a_output2[@]}"
+ echo -e "\n - remediation of kernel module: \"$l_mod_name\" complete\n"
+} +
+
+
+
+ + + + + + + +
+
+ + Configure Network Kernel Parameters + + The following network parameters are intended for use on both host only and router systems. A system acts as a router if it has at least two interfaces and is configured to perform routing functions. + + Notes: + + + +sysctl settings are defined through files in /usr/local/lib +, /usr/lib/ +, /lib/ +, /run/ +, and /etc/ + + +Files are typically placed in the sysctl.d + directory within the parent directory + +The paths where sysctl preload files usually exist + + + /run/sysctl.d/*.conf + + + /etc/sysctl.d/*.conf + + + /usr/local/lib/sysctl.d/*.conf + + + /usr/lib/sysctl.d/*.conf + + + /lib/sysctl.d/*.conf + + + /etc/sysctl.conf + + + + +Files must have the " .conf +" extension + +Vendors settings usually live in /usr/lib/ + or /usr/local/lib/ + + +To override a whole file, create a new file with the same name in /etc/sysctl.d/ + and put new settings there. + +To override only specific settings, add a file with a lexically later name in /etc/sysctl.d/ + and put new settings there. + +The command /usr/lib/systemd/systemd-sysctl --cat-config + produces output containing The system's loaded kernel parameters and the files they're configured in: + + Entries listed latter in the file take precedence over the same settings listed earlier in the file + Files containing kernel parameters that are over-ridden by other files with the same name will not be listed + On systems running UncomplicatedFirewall, the kernel parameters may be set or over-written. This will not be visible in the output of the command + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + +The settings in /etc/ufw/sysctl.conf + will override settings other settings and will not + be visible in the output of the /usr/lib/systemd/systemd-sysctl --cat-config + command + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + The system's loaded kernel parameters and the files they're configured in can be viewed by running the following command: + # /usr/lib/systemd/systemd-sysctl --cat-config + + + + Ensure ip forwarding is disabled + + +The net.ipv4.ip_forward + and net.ipv6.conf.all.forwarding + flags are used to tell the system whether it can forward packets or not. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +Setting net.ipv4.ip_forward + and net.ipv6.conf.all.forwarding + to 0 + ensures that a system with multiple interfaces (for example, a hard proxy), will never be able to forward packets, and therefore, never serve as a router. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.ip_forward = 0 + + + + Example: + + # printf '%s\n' "net.ipv4.ip_forward = 0" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv4.ip_forward=0
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + - IF - + IPv6 is enabled on the system: + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv6.conf.all.forwarding = 0 + + + + Example: + + # printf '%s\n' "net.ipv6.conf.all.forwarding = 0" >> /etc/sysctl.d/60-netipv6_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv6.conf.all.forwarding=0
+ sysctl -w net.ipv6.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten + Impact: + + IP forwarding is required on systems configured to act as a router. If these parameters are disabled, the system will not be able to perform as a router. + Many Cloud Service Provider (CSP) hosted systems require IP forwarding to be enabled. If the system is running on a CSP platform, this requirement should be reviewed before disabling IP forwarding. + +
+
+
+ + + + + + + + + + + + +
+ + Ensure packet redirect sending is disabled + + ICMP Redirects are used to send routing information to other hosts. As a host itself does not act as a router (in a host only configuration), there is no need to send redirects. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + An attacker could use a compromised host to send invalid ICMP redirects to other router devices in an attempt to corrupt routing and have users access a system set up by the attacker as opposed to a valid system. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.send_redirects = 0 + + + net.ipv4.conf.default.send_redirects = 0 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.send_redirects = 0" "net.ipv4.conf.default.send_redirects = 0" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv4.conf.all.send_redirects=0
+ sysctl -w net.ipv4.conf.default.send_redirects=0
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten + Impact: + + IP forwarding is required on systems configured to act as a router. If these parameters are disabled, the system will not be able to perform as a router. + +
+
+
+ + + + + + + + + + + + +
+ + Ensure bogus icmp responses are ignored + + +Setting net.ipv4.icmp_ignore_bogus_error_responses + to 1 + prevents the kernel from logging bogus responses (RFC-1122 non-compliant) from broadcast reframes, keeping file systems from filling up with useless log messages. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + Some routers (and some attackers) will send responses that violate RFC-1122 and attempt to fill up a log file system with many useless error messages. + + + + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.icmp_ignore_bogus_error_responses = 1 + + + + Example: + + # printf '%s\n' "net.ipv4.icmp_ignore_bogus_error_responses = 1" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv4.icmp_ignore_bogus_error_responses=1
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + +
+ + Ensure broadcast icmp requests are ignored + + +Setting net.ipv4.icmp_echo_ignore_broadcasts + to 1 + will cause the system to ignore all ICMP echo and timestamp requests to broadcast and multicast addresses. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + Accepting ICMP echo and timestamp requests with broadcast or multicast destinations for your network could be used to trick your host into starting (or participating) in a Smurf attack. A Smurf attack relies on an attacker sending large amounts of ICMP broadcast messages with a spoofed source address. All hosts receiving this message and responding would send echo-reply messages back to the spoofed address, which is probably not routable. If many hosts respond to the packets, the amount of traffic on the network could be significantly multiplied. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.icmp_echo_ignore_broadcasts = 1 + + + + Example: + + # printf '%s\n' "net.ipv4.icmp_echo_ignore_broadcasts = 1" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + +
+ + Ensure icmp redirects are not accepted + + ICMP redirect messages are packets that convey routing information and tell your host (acting as a router) to send packets via an alternate path. It is a way of allowing an outside routing device to update your system routing tables. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +ICMP redirect messages are packets that convey routing information and tell your host (acting as a router) to send packets via an alternate path. It is a way of allowing an outside routing device to update your system routing tables. By setting net.ipv4.conf.all.accept_redirects +, net.ipv4.conf.default.accept_redirects +, net.ipv6.conf.all.accept_redirects +, and net.ipv6.conf.default.accept_redirects + to 0 +, the system will not accept any ICMP redirect messages, and therefore, won't allow outsiders to update the system's routing tables. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.accept_redirects = 0 + + + net.ipv4.conf.default.accept_redirects = 0 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.accept_redirects = 0" "net.ipv4.conf.default.accept_redirects = 0" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv4.conf.all.accept_redirects=0
+ sysctl -w net.ipv4.conf.default.accept_redirects=0
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + - IF - + IPv6 is enabled on the system: + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv6.conf.all.accept_redirects = 0 + + + net.ipv6.conf.default.accept_redirects = 0 + + + + Example: + + # printf '%s\n' "net.ipv6.conf.all.accept_redirects = 0" "net.ipv6.conf.default.accept_redirects = 0" >> /etc/sysctl.d/60-netipv6_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv6.conf.all.accept_redirects=0
+ sysctl -w net.ipv6.conf.default.accept_redirects=0
+ sysctl -w net.ipv6.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure secure icmp redirects are not accepted + + Secure ICMP redirects are the same as ICMP redirects, except they come from gateways listed on the default gateway list. It is assumed that these gateways are known to your system, and that they are likely to be secure. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +It is still possible for even known gateways to be compromised. Setting net.ipv4.conf.all.secure_redirects + and net.ipv4.conf.default.secure_redirects + to 0 + protects the system from routing table updates by possibly compromised known gateways. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.secure_redirects = 0 + + + net.ipv4.conf.default.secure_redirects = 0 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.secure_redirects = 0" "net.ipv4.conf.default.secure_redirects = 0" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv4.conf.all.secure_redirects=0
+ sysctl -w net.ipv4.conf.default.secure_redirects=0
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + + + + + + +
+ + Ensure reverse path filtering is enabled + + +Setting net.ipv4.conf.all.rp_filter + and net.ipv4.conf.default.rp_filter + to 1 + forces the Linux kernel to utilize reverse path filtering on a received packet to determine if the packet was valid. Essentially, with reverse path filtering, if the return packet does not go out the same interface that the corresponding source packet came from, the packet is dropped (and logged if log_martians + is set). + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +Setting net.ipv4.conf.all.rp_filter + and net.ipv4.conf.default.rp_filter + to 1 + is a good way to deter attackers from sending your system bogus packets that cannot be responded to. One instance where this feature breaks down is if asymmetrical routing is employed. This would occur when using dynamic routing protocols (bgp, ospf, etc) on your system. If you are using asymmetrical routing on your system, you will not be able to enable this feature without breaking the routing. + + + + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.rp_filter = 1 + + + net.ipv4.conf.default.rp_filter = 1 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.rp_filter = 1" "net.ipv4.conf.default.rp_filter = 1" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv4.conf.all.rp_filter=1
+ sysctl -w net.ipv4.conf.default.rp_filter=1
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten + Impact: + + If you are using asymmetrical routing on your system, you will not be able to enable this feature without breaking the routing. + +
+
+
+ + + + + + + + + + + + +
+ + Ensure source routed packets are not accepted + + In networking, source routing allows a sender to partially or fully specify the route packets take through a network. In contrast, non-source routed packets travel a path determined by routers in the network. In some cases, systems may not be routable or reachable from some locations (e.g. private addresses vs. Internet routable), and so source routed packets would need to be used. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +Setting net.ipv4.conf.all.accept_source_route +, net.ipv4.conf.default.accept_source_route +, net.ipv6.conf.all.accept_source_route + and net.ipv6.conf.default.accept_source_route + to 0 + disables the system from accepting source routed packets. Assume this system was capable of routing packets to Internet routable addresses on one interface and private addresses on another interface. Assume that the private addresses were not routable to the Internet routable addresses and vice versa. Under normal routing circumstances, an attacker from the Internet routable addresses could not use the system as a way to reach the private address systems. If, however, source routed packets were allowed, they could be used to gain access to the private address systems as the route could be specified, rather than rely on routing protocols that did not allow this routing. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.accept_source_route = 0 + + + net.ipv4.conf.default.accept_source_route = 0 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.accept_source_route = 0" "net.ipv4.conf.default.accept_source_route = 0" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv4.conf.all.accept_source_route=0
+ sysctl -w net.ipv4.conf.default.accept_source_route=0
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + - IF - + IPv6 is enabled on the system: + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv6.conf.all.accept_source_route = 0 + + + net.ipv6.conf.default.accept_source_route = 0 + + + + Example: + + # printf '%s\n' "net.ipv6.conf.all.accept_source_route = 0" "net.ipv6.conf.default.accept_source_route = 0" >> /etc/sysctl.d/60-netipv6_sysctl.conf + + Run the following command to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv6.conf.all.accept_source_route=0
+ sysctl -w net.ipv6.conf.default.accept_source_route=0
+ sysctl -w net.ipv6.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure suspicious packets are logged + + When enabled, this feature logs packets with un-routable source addresses to the kernel log. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +Setting net.ipv4.conf.all.log_martians + and net.ipv4.conf.default.log_martians + to 1 + enables this feature. Logging these packets allows an administrator to investigate the possibility that an attacker is sending spoofed packets to their system. + + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.log_martians = 1 + + + net.ipv4.conf.default.log_martians = 1 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.log_martians = 1" "net.ipv4.conf.default.log_martians = 1" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv4.conf.all.log_martians=1
+ sysctl -w net.ipv4.conf.default.log_martians=1
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + + + + + + +
+ + Ensure tcp syn cookies is enabled + + +When tcp_syncookies + is set, the kernel will handle TCP SYN packets normally until the half-open connection queue is full, at which time, the SYN cookie functionality kicks in. SYN cookies work by not using the SYN queue at all. Instead, the kernel simply replies to the SYN with a SYN/ACK, but will include a specially crafted TCP sequence number that encodes the source and destination IP address and port number and the time the packet was sent. A legitimate connection would send the ACK packet of the three way handshake with the specially crafted sequence number. This allows the system to verify that it has received a valid response to a SYN cookie and allow the connection, even though there is no corresponding SYN in the queue. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +Attackers use SYN flood attacks to perform a denial of service attacked on a system by sending many SYN packets without completing the three way handshake. This will quickly use up slots in the kernel's half-open connection queue and prevent legitimate connections from succeeding. Setting net.ipv4.tcp_syncookies + to 1 + enables SYN cookies, allowing the system to keep accepting valid connections, even if under a denial of service attack. + + + + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.tcp_syncookies = 1 + + + + Example: + + # printf '%s\n' "net.ipv4.tcp_syncookies = 1" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv4.tcp_syncookies=1
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + +
+ + Ensure ipv6 router advertisements are not accepted + + Routers periodically multicast Router Advertisement messages to announce their availability and convey information to neighboring nodes that enable them to be automatically configured on the network. + + net.ipv6.conf.all.accept_ra + and net.ipv6.conf.default.accept_ra + determine the systems ability to accept these advertisements + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +It is recommended that systems do not accept router advertisements as they could be tricked into routing traffic to compromised machines. Setting hard routes within the system (usually a single default route to a trusted router) protects the system from bad routes. Setting net.ipv6.conf.all.accept_ra + and net.ipv6.conf.default.accept_ra + to 0 + disables the system's ability to accept IPv6 router advertisements. + + + + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + + - IF - + IPv6 is enabled on the system: + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv6.conf.all.accept_ra = 0 + + + net.ipv6.conf.default.accept_ra = 0 + + + + Example: + + # printf '%s\n' "net.ipv6.conf.all.accept_ra = 0" "net.ipv6.conf.default.accept_ra = 0" >> /etc/sysctl.d/60-netipv6_sysctl.conf + + Run the following script to set the active kernel parameters: + +#!/usr/bin/env bash
+
+{
+ sysctl -w net.ipv6.conf.all.accept_ra=0
+ sysctl -w net.ipv6.conf.default.accept_ra=0
+ sysctl -w net.ipv6.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + + + + + + +
+
+
+ + Host Based Firewall + + A Host Based Firewall, on a Linux system, is a set of rules used to protect machines from any unwanted traffic from outside. It enables users to +control incoming network traffic on host machines by defining a set of firewall rules. These rules are used to sort the incoming traffic and either block it or allow it through. + In order to configure firewall rules a firewall utility needs to be installed. Guidance has been included for the following firewall utilities: + + + + firewallD +: the firewalld utility can be used for simple firewall use cases. The firewalld utility is easy to use and covers typical use cases. + + + + nftables +: the nftables utility is often used to set up complex and performance-critical firewalls, such as a whole network. + + + +Only one + method should be used to configure a firewall on the system. Use of more than one method could produce unexpected results. + + Note: + + + This section is intended only to ensure the resulting firewall rules are in place, not how they are configured. + +The ipset + and iptables-nft + packages have been deprecated in Fedora 34 based Linux distributions. This includes deprecation of nft-variants such as iptables +, ip6tables +, arptables +, and ebtables + utilities. If you are using any of these tools, for example, because you upgraded from an earlier version, we recommend migrating to the nft command line tool provided by the nftables + package. + + firewalld + with nftables + backend does not support passing custom nftables + rules to firewalld +, using the --direct + option. + Allow port 22(ssh) needs to be updated to only allow systems requiring ssh connectivity to connect, as per site policy. + + + + Configure a firewall utility + + +In order to configure firewall rules a firewall utility needs to be installed either nftables + or firewalld + (with default nftables backend). + +Only one + method should be used to configure a firewall on the system. Use of more than one method could produce unexpected results. + + Note: + + + IPTables are deprecated in this release, and not covered in this Benchmark. If your firewall configuration still uses iptables rules, you should migrate your iptables rules to nftables. + Allow port 22(ssh) needs to be updated to only allow systems requiring ssh connectivity to connect, as per site policy. + + + + Ensure nftables is installed + + nftables provides a new in-kernel packet classification framework that is based on a network-specific Virtual Machine (VM) and a new nft userspace command line tool. + nftables reuses the existing Netfilter subsystems such as the existing hook infrastructure, the connection tracking system, NAT, userspace queuing and logging subsystem. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + nftables is a subsystem of the Linux kernel that can protect against threats originating from within a corporate network to include malicious mobile code and poorly configured software on a host. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + +Run the following command to install nftables + + # dnf install nftables + + Impact: + + Changing firewall settings while connected over the network can result in being locked out of the system. + + + + + + + + + + + + Ensure a single firewall configuration utility is in use + + In Linux security, employing a single, effective firewall configuration utility is crucial. Firewalls act as digital gatekeepers by filtering network traffic based on rules. Proper firewall configurations ensure that only legitimate traffic gets processed, reducing the system’s exposure to potential threats. The choice between FirewallD and NFTables depends on organizational specific needs: + + FirewallD + - Is a firewall service daemon that provides a dynamic customizable host-based firewall with a D-Bus interface. Being dynamic, it enables creating, changing, and deleting the rules without the necessity to restart the firewall daemon each time the rules are changed. + + NFTables + - Includes the nft utility for configuration of the nftables subsystem of the Linux kernel. + + Notes: + + + +firewalld with nftables backend does not support passing custom nftables rules to firewalld, using the --direct + option. + In order to configure firewall rules for nftables, a firewall utility needs to be installed and active of the system. The use of more than one firewall utility may produce unexpected results. + Allow port 22(ssh) needs to be updated to only allow systems requiring ssh connectivity to connect, as per site policy. + + + + + + + + Devices + Protect + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Proper configuration of a single firewall utility minimizes cyber threats and protects services and data, while avoiding vulnerabilities like open ports or exposed services. Standardizing on a single tool simplifies management, reduces errors, and fortifies security across Linux systems. + + + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/configuring_firewalls_and_packet_filters/index + + + + Run the following script to ensure that a single firewall utility is in use on the system: + +#!/usr/bin/env bash
+
+{
+ l_fwd_status="" l_nft_status="" l_fwutil_status=""
+ # Determine FirewallD utility Status
+ rpm -q firewalld > /dev/null 2>&1 && l_fwd_status="$(systemctl is-enabled firewalld.service):$(systemctl is-active firewalld.service)"
+ # Determine NFTables utility Status
+ rpm -q nftables > /dev/null 2>&1 && l_nft_status="$(systemctl is-enabled nftables.service):$(systemctl is-active nftables.service)"
+ l_fwutil_status="$l_fwd_status:$l_nft_status"
+ case $l_fwutil_status in
+ enabled:active:masked:inactive|enabled:active:disabled:inactive)
+ echo -e "\n - FirewallD utility is in use, enabled and active\n - NFTables utility is correctly disabled or masked and inactive\n - no remediation required" ;;
+ masked:inactive:enabled:active|disabled:inactive:enabled:active)
+ echo -e "\n - NFTables utility is in use, enabled and active\n - FirewallD utility is correctly disabled or masked and inactive\n - no remediation required" ;;
+ enabled:active:enabled:active)
+ echo -e "\n - Both FirewallD and NFTables utilities are enabled and active\n - stopping and masking NFTables utility"
+ systemctl stop nftables && systemctl --now mask nftables ;;
+ enabled:*:enabled:*)
+ echo -e "\n - Both FirewallD and NFTables utilities are enabled\n - remediating"
+ if [ "$(awk -F: '{print $2}' <<< "$l_fwutil_status")" = "active" ] && [ "$(awk -F: '{print $4}' <<< "$l_fwutil_status")" = "inactive" ]; then
+ echo " - masking NFTables utility"
+ systemctl stop nftables && systemctl --now mask nftables
+ elif [ "$(awk -F: '{print $4}' <<< "$l_fwutil_status")" = "active" ] && [ "$(awk -F: '{print $2}' <<< "$l_fwutil_status")" = "inactive" ]; then
+ echo " - masking FirewallD utility"
+ systemctl stop firewalld && systemctl --now mask firewalld
+ fi ;;
+ *:active:*:active)
+ echo -e "\n - Both FirewallD and NFTables utilities are active\n - remediating"
+ if [ "$(awk -F: '{print $1}' <<< "$l_fwutil_status")" = "enabled" ] && [ "$(awk -F: '{print $3}' <<< "$l_fwutil_status")" != "enabled" ]; then
+ echo " - stopping and masking NFTables utility"
+ systemctl stop nftables && systemctl --now mask nftables
+ elif [ "$(awk -F: '{print $3}' <<< "$l_fwutil_status")" = "enabled" ] && [ "$(awk -F: '{print $1}' <<< "$l_fwutil_status")" != "enabled" ]; then
+ echo " - stopping and masking FirewallD utility"
+ systemctl stop firewalld && systemctl --now mask firewalld
+ fi ;;
+ :enabled:active)
+ echo -e "\n - NFTables utility is in use, enabled, and active\n - FirewallD package is not installed\n - no remediation required" ;;
+ :)
+ echo -e "\n - Neither FirewallD or NFTables is installed.\n - remediating\n - installing NFTables"
+ echo -e "\n - Configure only ONE firewall either NFTables OR Firewalld and follow the according subsection to complete this remediation process"
+ dnf -q install nftables ;;
+ *:*:)
+ echo -e "\n - NFTables package is not installed on the system\n - remediating\n - installing NFTables"
+ echo -e "\n - Configure only ONE firewall either NFTables OR Firewalld and follow the according subsection to complete this remediation process"
+ dnf -q install nftables ;;
+ *)
+ echo -e "\n - Unable to determine firewall state"
+ echo -e "\n - MANUAL REMEDIATION REQUIRED: Configure only ONE firewall either NFTables OR Firewalld" ;;
+ esac
+} +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + Configure FirewallD + + + firewalld + uses the concepts of zones and services, that simplify the traffic management. Zones are +predefined sets of rules that cover all necessary settings to allow or deny incoming traffic for a specific service and zone. + + Important: + Configuration of a live systems firewall directly over a remote connection will often result in being locked out. It is advised to have a known good firewall configuration set to run on boot and to configure an entire firewall structure in a script that is then run and tested before saving to boot. + + Warning: + Only one method should be used to configure a firewall on the system. Use of more than one method could produce unexpected results. + + + Note: + - IF - + nftables + is being used this subsection should be skipped. + +The following example will create a firewalld + zone called securezone + to implement the firewall rules of this section leveraging the firewalld utility included with the firewalld package. This example will open port 22(ssh) from anywhere. Opening service SSH + should be updated in accordance with local site policy. If another name for the zone is preferred, replace securezone + with the name to be used. + + Sample securezone zone xml file + + +<?xml version="1.0" encoding="utf-8"?>
+<zone target="DROP">
+ <description>For use with CIS Linux Benchmark. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
+ <service name="ssh"/>
+ <service name="dhcpv6-client"/>
+ <icmp-block name="destination-unreachable"/>
+ <icmp-block name="packet-too-big"/>
+ <icmp-block name="time-exceeded"/>
+ <icmp-block name="parameter-problem"/>
+ <icmp-block name="neighbour-advertisement"/>
+ <icmp-block name="neighbour-solicitation"/>
+ <icmp-block name="router-advertisement"/>
+ <icmp-block name="router-solicitation"/>
+ <rule family="ipv4">
+ <source address="127.0.0.1"/>
+ <destination address="127.0.0.1" invert="True"/>
+ <drop/>
+ </rule>
+ <rule family="ipv6">
+ <source address="::1"/>
+ <destination address="::1" invert="True"/>
+ <drop/>
+ </rule>
+ <icmp-block-inversion/>
+</zone> +
+ + Note: + To use this zone, save this as /etc/firewalld/zones/securezone.xml + and run the following commands: + +# firewall-cmd --reload
+# firewall-cmd --permanent --zone=securezone --change-interface={NAME OF NETWORK INTERFACE} +
+
+ + Ensure firewalld drops unnecessary services and ports + + Services and ports can be accepted or explicitly rejected or dropped by a zone. + For every zone, you can set a default behavior that handles incoming traffic that is not further specified. Such behavior is defined by setting the target of the zone. There are three options - default, ACCEPT, REJECT, and DROP. + + ACCEPT - you accept all incoming packets except those disabled by a specific rule. + REJECT - you disable all incoming packets except those that you have allowed in specific rules and the source machine is informed about the rejection. + DROP - you disable all incoming packets except those that you have allowed in specific rules and no information sent to the source machine. + + + Note: + + + + - IF - + NFTables + is being used, this recommendation can be skipped. + Allow port 22(ssh) needs to be updated to only allow systems requiring ssh connectivity to connect, as per site policy. + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + To reduce the attack surface of a system, all services and ports should be blocked unless required + + + + firewalld.service(5) + NIST SP 800-53 Rev. 5: CA-9 + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/pdf/configuring_firewalls_and_packet_filters/red_hat_enterprise_linux-9-configuring_firewalls_and_packet_filters-en-us.pdf + + + + If Firewalld is in use on the system: + Run the following command to remove an unnecessary service: + # firewall-cmd --remove-service=<service> + + + Example: + + # firewall-cmd --remove-service=cockpit + + Run the following command to remove an unnecessary port: + # firewall-cmd --remove-port=<port-number>/<port-type> + + + Example: + + # firewall-cmd --remove-port=25/tcp + + Run the following command to make new settings persistent: + # firewall-cmd --runtime-to-permanent + + + + + + + Ensure firewalld loopback traffic is configured + + Configure the loopback interface to accept traffic. Configure all other interfaces to deny traffic to the loopback network + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Loopback traffic is generated between processes on machine and is typically critical to operation of the system. The loopback interface is the only place that loopback network traffic should be seen, all other interfaces should ignore traffic on this network as an anti-spoofing measure. + + + + NIST SP 800-53 Rev. 5: CA-9 + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/pdf/configuring_firewalls_and_packet_filters/red_hat_enterprise_linux-9-configuring_firewalls_and_packet_filters-en-us.pdf + + + + Run the following script to implement the loopback rules: + +#!/usr/bin/env bash
+
+{ l_hbfw=""
+ if systemctl is-enabled firewalld.service | grep -q 'enabled'; then
+ echo -e "\n - FirewallD is in use on the system" && l_hbfw="fwd"
+ elif systemctl is-enabled nftables.service 2>/dev/null | grep -q 'enabled'; then
+ echo -e "\n - nftables is in use on the system \n - Recommendation is NA \n - Remediation Complete" && l_hbfw="nft"
+ fi
+ if [ "$l_hbfw" = "fwd" ]; then
+ l_ipsaddr="$(nft list ruleset | awk '/filter_IN_public_deny|hook\s+input\s+/,/\}\s*(#.*)?$/' | grep -P -- 'ip\h+saddr')"
+ if ! nft list ruleset | awk '/hook\s+input\s+/,/\}\s*(#.*)?$/' | grep -Pq -- '\H+\h+"lo"\h+accept'; then
+ echo -e "\n - Enabling input to accept for loopback address"
+ firewall-cmd --permanent --zone=trusted --add-interface=lo
+ firewall-cmd --reload
+ else
+ echo -e "\n - firewalld input correctly set to accept for loopback address"
+ if ! grep -Pq -- 'ip\h+saddr\h+127\.0\.0\.0\/8\h+(counter\h+packets\h+\d+\h+bytes\h+\d+\h+)?drop' <<< "$l_ipsaddr" && ! grep -Pq -- 'ip\h+daddr\h+\!\=\h+127\.0\.0\.1\h+ip\h+saddr\h+127\.0\.0\.1\h+drop' <<< "$l_ipsaddr"; then
+ echo -e "\n - Setting IPv4 network traffic from loopback address to drop"
+ firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address="127.0.0.1" destination not address="127.0.0.1" drop'
+ firewall-cmd --permanent --zone=trusted --add-rich-rule='rule family=ipv4 source address="127.0.0.1" destination not address="127.0.0.1" drop'
+ firewall-cmd --reload
+ else
+ echo -e "\n - firewalld correctly set IPv4 network traffic from loopback address to drop"
+ fi
+ if grep -Pq -- '^\h*0\h*$' /sys/module/ipv6/parameters/disable; then
+ l_ip6saddr="$(nft list ruleset | awk '/filter_IN_public_deny|hook input/,/}/' | grep 'ip6 saddr')"
+ if ! grep -Pq 'ip6\h+saddr\h+::1\h+(counter\h+packets\h+\d+\h+bytes\h+\d+\h+)?drop' <<< "$l_ip6saddr" && ! grep -Pq -- 'ip6\h+daddr\h+\!=\h+::1\h+ip6\h+saddr\h+::1\h+drop' <<< "$l_ip6saddr"; then
+ echo -e "\n - Setting IPv6 network traffic from loopback address to drop"
+ firewall-cmd --permanent --add-rich-rule='rule family=ipv6 source address="::1" destination not address="::1" drop'
+ firewall-cmd --permanent --zone=trusted --add-rich-rule='rule family=ipv6 source address="::1" destination not address="::1" drop'
+ firewall-cmd --reload
+ else
+ echo -e "\n - firewalld correctly set IPv6 network traffic from loopback address to drop"
+ fi
+ fi
+ fi
+ fi
+} +
+
+
+
+ + + + + + +
+
+ + Configure NFTables + + +The nftables + framework classifies packets and it is the successor to the iptables +, ip6tables +, arptables +, ebtables +, and ipset + utilities. The nftables + framework uses tables to store chains. The chains contain individual rules for performing actions. The nft + utility replaces all tools from the previous packet-filtering frameworks. + + Important: + Configuration of a live systems firewall directly over a remote connection will often result in being locked out. It is advised to have a known good firewall configuration set to run on boot and to configure an entire firewall structure in a script that is then run and tested before saving to boot. + + Warning: + Only one method should be used to configure a firewall on the system. Use of more than one method could produce unexpected results. + + + Note: + - IF - + firewalld + is being used this subsection should be skipped. + +The following will implement the firewall rules of this section leveraging the nftables + utility included with the nftables + package. This example will open ICMP, IGMP, and port 22(ssh) from anywhere. Opening the ports for ICMP, IGMP, and port 22(ssh) needs to be updated in accordance with local site policy. Allow port 22(ssh) should to be updated to only allow systems requiring ssh connectivity to connect, as per site policy. + +Save the script below as /etc/nftables/nftables_rules.nft + + +#!/usr/sbin/nft -f
+
+# flush nftables rulesset
+flush ruleset
+
+# Load nftables ruleset
+# nftables config with inet table named filter
+
+table inet filter {
+ chain input {
+ type filter hook input priority 0; policy drop;
+
+ # allow loopback if not forged
+ iif lo accept
+ iif != lo ip saddr 127.0.0.1/8 drop
+ iif != lo ip6 saddr ::1/128 drop
+
+ # allow connections made by ourselves
+ ip protocol tcp ct state established accept
+ ip protocol udp ct state established accept
+ ip protocol icmp ct state established accept
+
+ # allow from anywhere
+ ip protocol igmp accept
+ tcp dport ssh accept
+
+ # allow some icmp
+ icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept
+ icmp type { destination-unreachable, router-advertisement, router-solicitation, time-exceeded, parameter-problem } accept
+ }
+
+ chain forward {
+ # drop all forward
+ type filter hook forward priority 0; policy drop;
+ }
+
+ chain output {
+ # can omit this as its accept by default
+ type filter hook output priority 0; policy accept;
+ }
+} +
+ Run the following command to run nftables script by passing it to the nft utility: + # nft -f /etc/nftables/nftables_rules.nft + + + Note: + All changes in the nftables subsections are temporary + To make these changes permanent and automatically load nftables rules when the system boots: + +Add the following line to /etc/sysconfig/nftables.conf + + include "/etc/nftables/nftables_rules.nft" + +
+ + Ensure nftables base chains exist + + Chains are containers for rules. They exist in two kinds, base chains and regular chains. A base chain is an entry point for packets from the networking stack, a regular chain may be used as jump target and is used for better rule organization. + + Note: + - IF - + Firewalld + is in use, this recommendation can be skipped. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If a base chain doesn't exist with a hook for input, forward, and delete, packets that would flow through those chains will not be touched by nftables. + + + + NIST SP 800-53 Rev. 5: CA-9 + https://www.netfilter.org/projects/nftables/manpage.html + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/pdf/configuring_firewalls_and_packet_filters/red_hat_enterprise_linux-9-configuring_firewalls_and_packet_filters-en-us.pdf + + + + + - IF - + NFTables + utility is in use on your system: + Run the following command to create the base chains: + # nft create chain inet <table name> <base chain name> { type filter hook <(input|forward|output)> priority 0 \; } + + + Example: + + +# nft create chain inet filter input { type filter hook input priority 0 \; }
+# nft create chain inet filter forward { type filter hook forward priority 0 \; }
+# nft create chain inet filter output { type filter hook output priority 0 \; } +
+ + Note: + use the add + command if the create + command returns an error due to the chain already existing. + Impact: + + +If configuring over ssh, creating + a base chain + with a policy of drop + will cause loss of connectivity. + Ensure that a rule allowing ssh has been added to the base chain prior to setting the base chain's policy to drop + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure nftables established connections are configured + + Configure the firewall rules for new outbound and established connections + + Note: + - IF - + Firewalld + is in use, this recommendation can be skipped. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If rules are not in place for established connections, all packets will be dropped by the default policy preventing network usage. + + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/pdf/configuring_firewalls_and_packet_filters/red_hat_enterprise_linux-9-configuring_firewalls_and_packet_filters-en-us.pdf + NIST SP 800-53 Rev. 5: CA-9 + + + + + - IF - + NFTables + utility is in use on your system: + Configure nftables in accordance with site policy. The following commands will implement a policy to allow all established connections: + +# systemctl is-enabled nftables.service | grep -q 'enabled' && nft add rule inet filter input ip protocol tcp ct state established accept
+# systemctl is-enabled nftables.service | grep -q 'enabled' && nft add rule inet filter input ip protocol udp ct state established accept
+# systemctl is-enabled nftables.service | grep -q 'enabled' && nft add rule inet filter input ip protocol icmp ct state established accept +
+
+
+
+
+ + Ensure nftables default deny firewall policy + + Base chain policy is the default verdict that will be applied to packets reaching the end of the chain. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +There are two policies: accept (Default) and drop. If the policy is set to accept +, the firewall will accept any packet that is not configured to be denied and the packet will continue traversing the network stack. + It is easier to explicitly permit acceptable usage than to deny unacceptable usage. + + Note: + + + + - IF - + Firewalld + is in use, this recommendation can be skipped. + Changing firewall settings while connected over the network can result in being locked out of the system. + + + + + Manual Page nft + NIST SP 800-53 Rev. 5: CA-9 + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/pdf/configuring_firewalls_and_packet_filters/red_hat_enterprise_linux-9-configuring_firewalls_and_packet_filters-en-us.pdf + + + + + - IF - + NFTables + utility is in use on your system: + Run the following command for the base chains with the input, forward, and output hooks to implement a default DROP policy: + # nft chain <table family> <table name> <chain name> { policy drop \; } + + + Example: + + +# nft chain inet filter input { policy drop \; }
+# nft chain inet filter forward { policy drop \; } +
+ Impact: + + If configuring nftables over ssh, creating a base chain with a policy of drop will cause loss of connectivity. + + +Ensure that a rule allowing ssh + has been added to the base chain prior to setting the base chain's policy to drop + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure nftables loopback traffic is configured + + Configure the loopback interface to accept traffic. Configure all other interfaces to deny traffic to the loopback network + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Loopback traffic is generated between processes on machine and is typically critical to operation of the system. The loopback interface is the only place that loopback network traffic should be seen, all other interfaces should ignore traffic on this network as an anti-spoofing measure. + + + + NIST SP 800-53 Rev. 5: CA-9 + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/pdf/configuring_firewalls_and_packet_filters/red_hat_enterprise_linux-9-configuring_firewalls_and_packet_filters-en-us.pdf + + + + Run the following script to implement the loopback rules: + +#!/usr/bin/env bash
+
+{ l_hbfw=""
+ if systemctl is-enabled firewalld.service 2>/dev/null | grep -q 'enabled'; then
+ echo -e "\n - FirewallD is in use on the system\n - Recommendation is NA \n - Remediation Complete" && l_hbfw="fwd"
+ elif systemctl is-enabled nftables.service | grep -q 'enabled'; then
+ l_hbfw="nft"
+ fi
+ if [ "$l_hbfw" = "nft" ]; then
+ l_ipsaddr="$(nft list ruleset | awk '/filter_IN_public_deny|hook\s+input\s+/,/\}\s*(#.*)?$/' | grep -P -- 'ip\h+saddr')"
+ if ! nft list ruleset | awk '/hook\s+input\s+/,/\}\s*(#.*)?$/' | grep -Pq -- '\H+\h+"lo"\h+accept'; then
+ echo -e "\n - Enabling input to accept for loopback address"
+ nft add rule inet filter input iif lo accept
+ else
+ echo -e "\n -nftables input correctly configured to accept for loopback address"
+ fi
+ if ! grep -Pq -- 'ip\h+saddr\h+127\.0\.0\.0\/8\h+(counter\h+packets\h+\d+\h+bytes\h+\d+\h+)?drop' <<< "$l_ipsaddr" && ! grep -Pq -- 'ip\h+daddr\h+\!\=\h+127\.0\.0\.1\h+ip\h+saddr\h+127\.0\.0\.1\h+drop' <<< "$l_ipsaddr"; then
+ echo -e "\n - Setting IPv4 network traffic from loopback address to drop"
+ nft add rule inet filter input ip saddr 127.0.0.0/8 counter drop
+ else
+ echo -e "\n -nftables correctly configured IPv4 network traffic from loopback address to drop"
+ fi
+ if grep -Pq -- '^\h*0\h*$' /sys/module/ipv6/parameters/disable; then
+ l_ip6saddr="$(nft list ruleset | awk '/filter_IN_public_deny|hook input/,/}/' | grep 'ip6 saddr')"
+ if ! grep -Pq 'ip6\h+saddr\h+::1\h+(counter\h+packets\h+\d+\h+bytes\h+\d+\h+)?drop' <<< "$l_ip6saddr" && ! grep -Pq -- 'ip6\h+daddr\h+\!=\h+::1\h+ip6\h+saddr\h+::1\h+drop' <<< "$l_ip6saddr"; then
+ echo -e "\n - Setting IPv6 network traffic from loopback address to drop"
+ nft add rule inet filter input ip6 saddr ::1 counter drop
+ else
+ echo -e "\n - nftables IPv6 network traffic from loopback address to drop"
+ fi
+ fi
+ fi
+} +
+
+
+
+ + + + + + +
+
+
+ + Access Control + + + Configure SSH Server + + +Secure Shell (SSH) is a secure, encrypted replacement for common login services such as telnet +, ftp +, rlogin +, rsh +, and rcp +. It is strongly recommended that sites abandon older clear-text login protocols and use SSH to prevent session hijacking and sniffing of sensitive data off the network. + + Note: + + + +The recommendations in this section only apply if the SSH daemon is installed on the system, if remote access is not required the SSH daemon can be removed and this section skipped +. + +The openSSH daemon configuration directives, Include + and Match +, may cause the audits in this section's recommendations to report incorrectly. It is recommended that these options only be used if they're needed and fully understood. If these options are configured in accordance with local site policy, they should be accounted for when following the recommendations in this section. + +The default Include + location is the /etc/ssh/sshd_config.d + directory. This default has been accounted for in this section. If a file has an additional Include + that isn't this default location, the files should be reviewed to verify that the recommended setting is not being over-ridden. + +The audits of the running configuration in this section are run in the context of the root user, the local host name, and the local host's IP address. If a Match + block exists that matches one of these criteria, the output of the audit will be from the match block. The respective matched criteria should be replaced with a non-matching substitution. + + Include +: + + Include the specified configuration file(s). + Multiple pathnames may be specified and each pathname may contain glob(7) wildcards that will be expanded and processed in lexical order. + +Files without absolute paths are assumed to be in /etc/ssh/ +. + An Include directive may appear inside a Match block to perform conditional inclusion. + + + + Match +: + + Introduces a conditional block. If all of the criteria on the Match line are satisfied, the keywords on the following lines override those set in the global section of the config file, until either another Match line or the end of the file. If a keyword appears in multiple Match blocks that are satisfied, only the first instance of the keyword is applied. + The arguments to Match are one or more criteria-pattern pairs or the single token All which matches all criteria. The available criteria are User, Group, Host, LocalAddress, LocalPort, and Address. + The match patterns may consist of single entries or comma-separated lists and may use the wildcard and negation operators described in the PATTERNS section of ssh_config(5). + +The patterns in an Address criteria may additionally contain addresses to match in CIDR address/masklen format, such as 192.0.2.0/24 + or 2001:db8::/32 +. Note that the mask length provided must be consistent with the address - it is an error to specify a mask length that is too long for the address or one with bits set in this host portion of the address. For example, 192.0.2.0/33 + and 192.0.2.0/8 +, respectively. + Only a subset of keywords may be used on the lines following a Match keyword. Available keywords are available in the ssh_config man page. + + + +Once all configuration changes have been made to /etc/ssh/sshd_config + or any included configuration files, the sshd + configuration must be reloaded + + Command to re-load the SSH daemon configuration: + # systemctl reload-or-restart sshd + + + + Ensure permissions on /etc/ssh/sshd_config are configured + + +The file /etc/ssh/sshd_config +, and files ending in .conf + in the /etc/ssh/sshd_config.d + directory, contain configuration specifications for sshd +. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +configuration specifications for sshd + need to be protected from unauthorized changes by non-privileged users. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + +Run the following script to set ownership and permissions on /etc/ssh/sshd_config + and files ending in .conf + in the /etc/ssh/sshd_config.d + directory: + +#!/usr/bin/env bash
+
+{
+ chmod u-x,og-rwx /etc/ssh/sshd_config
+ chown root:root /etc/ssh/sshd_config
+ while IFS= read -r -d $'\0' l_file; do
+ if [ -e "$l_file" ]; then
+ chmod u-x,og-rwx "$l_file"
+ chown root:root "$l_file"
+ fi
+ done < <(find /etc/ssh/sshd_config.d -type f -print0 2>/dev/null)
+} +
+ + - IF - + other locations are listed in an Include + statement, *.conf + files in these locations access should also be modified. +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on SSH private host key files are configured + + An SSH private key is one of two files used in SSH public key authentication. In this authentication method, the possession of the private key is proof of identity. Only a private key that corresponds to a public key will be able to authenticate successfully. The private keys need to be stored and handled carefully, and no copies of the private key should be distributed. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + If an unauthorized user obtains the private SSH host key file, the host could be impersonated + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + Run the following script to set mode, ownership, and group on the private SSH host key files: + +{
+ l_output="" l_output2=""
+ l_ssh_group_name="$(awk -F: '($1 ~ /^(ssh_keys|_?ssh)$/) {print $1}' /etc/group)"
+ f_file_access_fix()
+ {
+ while IFS=: read -r l_file_mode l_file_owner l_file_group; do
+ echo "File: \"$l_file\" mode: \"$l_file_mode\" owner \"$l_file_owner\" group \"$l_file_group\""
+ l_out2=""
+ [ "$l_file_group" = "$l_ssh_group_name" ] && l_pmask="0137" || l_pmask="0177"
+ l_maxperm="$( printf '%o' $(( 0777 & ~$l_pmask )) )"
+ if [ $(( $l_file_mode & $l_pmask )) -gt 0 ]; then
+ l_out2="$l_out2\n - Mode: \"$l_file_mode\" should be mode: \"$l_maxperm\" or more restrictive\n - updating to mode: \:$l_maxperm\""
+ if [ "l_file_group" = "$l_ssh_group_name" ]; then
+ chmod u-x,g-wx,o-rwx "$l_file"
+ else
+ chmod u-x,go-rwx "$l_file"
+ fi
+ fi
+ if [ "$l_file_owner" != "root" ]; then
+ l_out2="$l_out2\n - Owned by: \"$l_file_owner\" should be owned by \"root\"\n - Changing ownership to \"root\""
+ chown root "$l_file"
+ fi
+ if [[ ! "$l_file_group" =~ ($l_ssh_group_name|root) ]]; then
+ [ -n "$l_ssh_group_name" ] && l_new_group="$l_ssh_group_name" || l_new_group="root"
+ l_out2="$l_out2\n - Owned by group \"$l_file_group\" should be group owned by: \"$l_ssh_group_name\" or \"root\"\n - Changing group ownership to \"$l_new_group\""
+ chgrp "$l_new_group" "$l_file"
+ fi
+ if [ -n "$l_out2" ]; then
+ l_output2="$l_output2\n - File: \"$l_file\"$l_out2"
+ else
+ l_output="$l_output\n - File: \"$l_file\"\n - Correct: mode: \"$l_file_mode\", owner: \"$l_file_owner\", and group owner: \"$l_file_group\" configured"
+ fi
+ done < <(stat -Lc '%#a:%U:%G' "$l_file")
+ }
+ while IFS= read -r -d $'\0' l_file; do
+ if ssh-keygen -lf &>/dev/null "$l_file"; then
+ file "$l_file" | grep -Piq -- '\bopenssh\h+([^#\n\r]+\h+)?private\h+key\b' && f_file_access_fix
+ fi
+ done < <(find -L /etc/ssh -xdev -type f -print0 2>/dev/null)
+ if [ -z "$l_output2" ]; then
+ echo -e "\n- No access changes required\n"
+ else
+ echo -e "\n- Remediation results:\n$l_output2\n"
+ fi
+} +
+
+
+
+ + + + + + + + + +
+ + Ensure permissions on SSH public host key files are configured + + An SSH public key is one of two files used in SSH public key authentication. In this authentication method, a public key is a key that can be used for verifying digital signatures generated using a corresponding private key. Only a public key that corresponds to a private key will be able to authenticate successfully. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + If a public host key file is modified by an unauthorized user, the SSH service may be compromised. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + Run the following script to set mode, ownership, and group on the public SSH host key files: + +#!/usr/bin/env bash
+
+{
+ l_output="" l_output2=""
+ l_pmask="0133" && l_maxperm="$( printf '%o' $(( 0777 & ~$l_pmask )) )"
+ FILE_ACCESS_FIX()
+ {
+ while IFS=: read -r l_file_mode l_file_owner l_file_group; do
+ l_out2=""
+ if [ $(( $l_file_mode & $l_pmask )) -gt 0 ]; then
+ l_out2="$l_out2\n - Mode: \"$l_file_mode\" should be mode: \"$l_maxperm\" or more restrictive\n - updating to mode: \:$l_maxperm\""
+ chmod u-x,go-wx
+ fi
+ if [ "$l_file_owner" != "root" ]; then
+ l_out2="$l_out2\n - Owned by: \"$l_file_owner\" should be owned by \"root\"\n - Changing ownership to \"root\""
+ chown root "$l_file"
+ fi
+ if [ "$l_file_group" != "root" ]; then
+ l_out2="$l_out2\n - Owned by group \"$l_file_group\" should be group owned by: \"root\"\n - Changing group ownership to \"root\""
+ chgrp root "$l_file"
+ fi
+ if [ -n "$l_out2" ]; then
+ l_output2="$l_output2\n - File: \"$l_file\"$l_out2"
+ else
+ l_output="$l_output\n - File: \"$l_file\"\n - Correct: mode: \"$l_file_mode\", owner: \"$l_file_owner\", and group owner: \"$l_file_group\" configured"
+ fi
+ done < <(stat -Lc '%#a:%U:%G' "$l_file")
+ }
+ while IFS= read -r -d $'\0' l_file; do
+ if ssh-keygen -lf &>/dev/null "$l_file"; then
+ file "$l_file" | grep -Piq -- '\bopenssh\h+([^#\n\r]+\h+)?public\h+key\b' && FILE_ACCESS_FIX
+ fi
+ done < <(find -L /etc/ssh -xdev -type f -print0 2>/dev/null)
+ if [ -z "$l_output2" ]; then
+ echo -e "\n- No access changes required\n"
+ else
+ echo -e "\n- Remediation results:\n$l_output2\n"
+ fi
+} +
+
+
+
+ + + + + + + + + +
+ + Ensure sshd Ciphers are configured + + This variable limits the ciphers that SSH can use during communication. + + Notes: + + + Some organizations may have stricter requirements for approved ciphers. + Ensure that ciphers used are in compliance with site policy. + +The only "strong" ciphers currently FIPS 140 compliant are: + + + aes256-gcm@openssh.com + + + aes128-gcm@openssh.com + + aes256-ctr + aes192-ctr + aes128-ctr + + + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Weak ciphers that are used for authentication to the cryptographic module cannot be relied upon to provide confidentiality or integrity, and system data may be compromised. + + The Triple DES ciphers, as used in SSH, have a birthday bound of approximately four billion blocks, which makes it easier for remote attackers to obtain clear text data via a birthday attack against a long-duration encrypted session, aka a "Sweet32" attack. + Error handling in the SSH protocol; Client and Server, when using a block cipher algorithm in Cipher Block Chaining (CBC) mode, makes it easier for remote attackers to recover certain plain text data from an arbitrary block of cipher text in an SSH session via unknown vectors. + + + + + https://nvd.nist.gov/vuln/detail/CVE-2023-48795 + https://nvd.nist.gov/vuln/detail/CVE-2019-1543 + https://nvd.nist.gov/vuln/detail/CVE-2016-2183 + https://nvd.nist.gov/vuln/detail/CVE-2008-5161 + https://www.openssh.com/txt/cbc.adv + https://www.openssh.com/txt/cbc.adv + SSHD_CONFIG(5) + SSHD(8) + NIST SP 800-53 Rev. 5: SC-8 + + + + + Note: + + + First occurrence of an option takes precedence. + +Though ciphers may be configured through the Ciphers + option in the /etc/ssh/sshd_config + file, it is recommended that the ciphers available to openSSH server are configured through system-wide-crypto-policy + If the recommendations in the subsection "Configure system wide crypto policy" have been followed, this Audit should be in a passing state. Please review that section before following this Remediation Procedure + +By default, system-wide-crypto-policy is applied to the openSSH server. If the following defaults don't exist due to modifications or upgrade from a earlier release, the system-wide-crypto-policy may not be included by the openSSH server. It is recommended that these defaults be restored, created, or the line Include /etc/crypto-policies/back-ends/opensshserver.config + be added before any lines containing the Cipher + argument. + +Defaults: + + +The file /etc/ssh/sshd_config + includes the line: Include /etc/ssh/sshd_config.d/*.conf +. This line must appear before any lines containing the Cipher + argument + +This directory /etc/ssh/sshd_config.d/ + includes a file /etc/ssh/sshd_config.d/50-redhat.conf + + +The file /etc/ssh/sshd_config.d/50-redhat.conf + includes the line Include /etc/crypto-policies/back-ends/opensshserver.config + + +The file /etc/crypto-policies/back-ends/opensshserver.config + is generated by system-wide-crypto-policy + + + + + - IF - + CVE-2023-48795 + has been addressed, and it meets local site policy, chacha20-poly1305 + may be removed from the list of excluded ciphers. + +Create or edit a file in /etc/crypto-policies/policies/modules/ + ending in .pmod + and add or modify the the following line: + cipher@SSH = -3DES-CBC -AES-128-CBC -AES-192-CBC -AES-256-CBC -CHACHA20-POLY1305 + + + Example: + + # printf '%s\n' "# This is a subpolicy to disable weak ciphers" "# for the SSH protocol (libssh and OpenSSH)" "cipher@SSH = -3DES-CBC -AES-128-CBC -AES-192-CBC -AES-256-CBC -CHACHA20-POLY1305" >> /etc/crypto-policies/policies/modules/NO-SSHWEAKCIPHERS.pmod + + Run the following command to update the system-wide cryptographic policy + # update-crypto-policies --set <CRYPTO_POLICY>:<CRYPTO_SUBPOLICY1>:<CRYPTO_SUBPOLICY2>:<CRYPTO_SUBPOLICY3> + + + Example: + + # update-crypto-policies --set DEFAULT:NO-SHA1:NO-WEAKMAC:NO-SSHCBC:NO-SSHCHACHA20:NO-SSHWEAKCIPHERS + + Run the following command to reload the openSSH server to make your cryptographic settings effective: + # systemctl reload-or-restart sshd + + + - OR - + If system-wide-crypto-policy is not being used to configure available ciphers ( This is not recommended +) + +Edit the /etc/ssh/sshd_config file and add/modify the Ciphers + line to contain a comma separated list of the site unapproved (weak) Ciphers preceded with a - + above any Include + entries: + + Example: + + Ciphers -3des-cbc,aes128-cbc,aes192-cbc,aes256-cbc,chacha20-poly1305@openssh.com + + + + + + + + + + + + + + + + + Ensure sshd KexAlgorithms is configured + + Key exchange is any method in cryptography by which cryptographic keys are exchanged between two parties, allowing use of a cryptographic algorithm. If the sender and receiver wish to exchange encrypted messages, each must be equipped to encrypt messages to be sent and decrypt messages received + + Notes: + + + Kex algorithms have a higher preference the earlier they appear in the list + Some organizations may have stricter requirements for approved Key exchange algorithms + Ensure that Key exchange algorithms used are in compliance with site policy + +The only Key Exchange Algorithms currently FIPS 140 approved are: + + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group16-sha512 + diffie-hellman-group18-sha512 + diffie-hellman-group14-sha256 + + + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + The supported algorithms are: + +curve25519-sha256
+curve25519-sha256@libssh.org
+diffie-hellman-group1-sha1
+diffie-hellman-group14-sha1
+diffie-hellman-group14-sha256
+diffie-hellman-group16-sha512
+diffie-hellman-group18-sha512
+diffie-hellman-group-exchange-sha1
+diffie-hellman-group-exchange-sha256
+ecdh-sha2-nistp256
+ecdh-sha2-nistp384
+ecdh-sha2-nistp521
+sntrup4591761x25519-sha512@tinyssh.org +
+
+
+ + Key exchange methods that are considered weak should be removed. A key exchange method may be weak because too few bits are used, or the hashing algorithm is considered too weak. Using weak algorithms could expose connections to man-in-the-middle attacks + + + + https://ubuntu.com/server/docs/openssh-crypto-configuration + NIST SP 800-53 Rev. 5: SC-8 + SSHD(8) + SSHD_CONFIG(5) + + + + + Note: + + + First occurrence of an option takes precedence. + +Though key_exchange may be configured through the KexAlgorithms + option in the /etc/ssh/sshd_config + file, it is recommended that the key_exchange available to openSSH server are configured through system-wide-crypto-policy + If the recommendations in the subsection "Configure system wide crypto policy" have been followed, this Audit should be in a passing state. Please review that section before following this Remediation Procedure + +By default, system-wide-crypto-policy is applied to the openSSH server. If the following defaults don't exist due to modifications or upgrade from a earlier release, the system-wide-crypto-policy may not be included by the openSSH server. It is recommended that these defaults be restored, created, or the line Include /etc/crypto-policies/back-ends/opensshserver.config + be added before any lines containing the KexAlgorithms + argument. + +Defaults: + + +The file /etc/ssh/sshd_config + includes the line: Include /etc/ssh/sshd_config.d/*.conf +. This line must appear before any lines containing the KexAlgorithms + argument + +This directory /etc/ssh/sshd_config.d/ + includes a file /etc/ssh/sshd_config.d/50-redhat.conf + + +The file /etc/ssh/sshd_config.d/50-redhat.conf + includes the line Include /etc/crypto-policies/back-ends/opensshserver.config + + +The file /etc/crypto-policies/back-ends/opensshserver.config + is generated by system-wide-crypto-policy + + + + Follow the Remediation Procedure in "Ensure system wide crypto policy disables sha1 hash and signature support" + + This is and excerpt of the Remediation Procedure from "Ensure system wide crypto policy disables sha1 hash and signature support": + + +Create or edit a file in /etc/crypto-policies/policies/modules/ + ending in .pmod + and add or modify the following lines: + +hash = -SHA1
+sign = -*-SHA1
+sha1_in_certs = 0 +
+ + Example: + + # printf '%s\n' "# This is a subpolicy dropping the SHA1 hash and signature support" "hash = -SHA1" "sign = -*-SHA1" "sha1_in_certs = 0" >> /etc/crypto-policies/policies/modules/NO-SHA1.pmod + + Run the following command to update the system-wide cryptographic policy + # update-crypto-policies --set <CRYPTO_POLICY>:<CRYPTO_SUBPOLICY1>:<CRYPTO_SUBPOLICY2>:<CRYPTO_SUBPOLICY3> + + + Example: + + # update-crypto-policies --set DEFAULT:NO-SHA1:NO-WEAKMAC:NO-SSHCBC:NO-SSHCHACHA20:NO-SSHETM:NO-SSHWEAKCIPHERS + + Run the following command to reload the openSSH server to make your cryptographic settings effective: + # systemctl reload-or-restart sshd + + + - OR - + If system-wide-crypto-policy is not being used to configure available KexAlgorithms ( This is not recommended +) + +Edit the /etc/ssh/sshd_config + file and add/modify the KexAlgorithms + line to contain a comma separated list of the site unapproved (weak) KexAlgorithms preceded with a - + above any Include + entries: + + Example: + + KexAlgorithms -diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1 + +
+
+
+ + + + + + + + + + +
+ + Ensure sshd MACs are configured + + This variable limits the types of MAC algorithms that SSH can use during communication. + + Notes: + + + Some organizations may have stricter requirements for approved MACs. + Ensure that MACs used are in compliance with site policy. + +The only "strong" MACs currently FIPS 140 approved are: + + HMAC-SHA1 + HMAC-SHA2-256 + HMAC-SHA2-384 + HMAC-SHA2-512 + + + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + Users + Protect + + + + + + MD5 and 96-bit MAC algorithms are considered weak and have been shown to increase exploitability in SSH downgrade attacks. Weak algorithms continue to have a great deal of attention as a weak spot that can be exploited with expanded computing power. An attacker that breaks the algorithm could take advantage of a MiTM position to decrypt the SSH tunnel and capture credentials and information. + + + + + https://nvd.nist.gov/vuln/detail/CVE-2023-48795 + More information on SSH downgrade attacks can be found here: http://www.mitls.org/pages/attacks/SLOTH + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + + Note: + + + First occurrence of an option takes precedence. + +Though MACs may be configured through the MACs + option in the /etc/ssh/sshd_config + file, it is recommended that the MACs available to openSSH server are configured through system-wide-crypto-policy + If the recommendations in the subsection "Configure system wide crypto policy" have been followed, this Audit should be in a passing state. Please review that section before following this Remediation Procedure + +By default, system-wide-crypto-policy is applied to the openSSH server. If the following defaults don't exist due to modifications or upgrade from a earlier release, the system-wide-crypto-policy may not be included by the openSSH server. It is recommended that these defaults be restored, created, or the line Include /etc/crypto-policies/back-ends/opensshserver.config + be added before any lines containing the MACs + argument. + +Defaults: + + +The file /etc/ssh/sshd_config + includes the line: Include /etc/ssh/sshd_config.d/*.conf +. This line must appear before any lines containing the MACs + argument + +This directory /etc/ssh/sshd_config.d/ + includes a file /etc/ssh/sshd_config.d/50-redhat.conf + + +The file /etc/ssh/sshd_config.d/50-redhat.conf + includes the line Include /etc/crypto-policies/back-ends/opensshserver.config + + +The file /etc/crypto-policies/back-ends/opensshserver.config + is generated by system-wide-crypto-policy + + + + + - IF - + CVE-2023-48795 + has not been reviewed and addressed, Recommendation "Ensure system wide crypto policy disables EtM for ssh" should be followed. + +Create or edit a file in /etc/crypto-policies/policies/modules/ + ending in .pmod + and add or modify the the following line: + mac@SSH = -HMAC-MD5* -UMAC-64* -UMAC-128* + + + Example: + + # printf '%s\n' "# This is a subpolicy to disable weak MACs" "# for the SSH protocol (libssh and OpenSSH)" "mac@SSH = -HMAC-MD5* -UMAC-64* -UMAC-128*" >> /etc/crypto-policies/policies/modules/NO-SSHWEAKMACS.pmod + + Run the following command to update the system-wide cryptographic policy + # update-crypto-policies --set <CRYPTO_POLICY>:<CRYPTO_SUBPOLICY1>:<CRYPTO_SUBPOLICY2>:<CRYPTO_SUBPOLICY3> + + + Example: + + # update-crypto-policies --set DEFAULT:NO-SHA1:NO-WEAKMAC:NO-SSHCBC:NO-SSHCHACHA20:NO-SSHETM:NO-SSHWEAKCIPHERS:NO-SSHWEAKMACS + + Run the following command to reload the openSSH server to make your cryptographic settings effective: + # systemctl reload-or-restart sshd + + + - OR - + If system-wide-crypto-policy is not being used to configure available ciphers ( This is not recommended +) + +Edit the /etc/ssh/sshd_config + file and add/modify the MACs + line to contain a comma separated list of the site unapproved (weak) MACs preceded with a - + above any Include + entries: + + Example: + + MACs -hmac-md5,hmac-md5-96,hmac-ripemd160,hmac-sha1-96,umac-64@openssh.com,hmac-md5-etm@openssh.com,hmac-md5-96-etm@openssh.com,hmac-ripemd160-etm@openssh.com,hmac-sha1-96-etm@openssh.com,umac-64-etm@openssh.com,umac-128-etm@openssh.com + + + - IF - + CVE-2023-48795 + has not been reviewed and addressed, the following etm + MACs should be added to the exclude list: hmac-sha1-etm@openssh.com +, hmac-sha2-256-etm@openssh.com +, hmac-sha2-512-etm@openssh.com + + + + + + + + + + + + + + + + + Ensure sshd access is configured + + There are several options available to limit which users and group can access the system via SSH. It is recommended that at least one of the following options be leveraged: + + + AllowUsers +: + + +The AllowUsers + variable gives the system administrator the option of allowing specific users to ssh + into the system. The list consists of space separated user names. Numeric user IDs are not recognized with this variable. If a system administrator wants to restrict user access further by only allowing the allowed users to log in from a particular host, the entry can be specified in the form of user@host. + + + + AllowGroups +: + + +The AllowGroups + variable gives the system administrator the option of allowing specific groups of users to ssh + into the system. The list consists of space separated group names. Numeric group IDs are not recognized with this variable. + + + + DenyUsers +: + + +The DenyUsers + variable gives the system administrator the option of denying specific users to ssh + into the system. The list consists of space separated user names. Numeric user IDs are not recognized with this variable. If a system administrator wants to restrict user access further by specifically denying a user's access from a particular host, the entry can be specified in the form of user@host. + + + + DenyGroups +: + + +The DenyGroups + variable gives the system administrator the option of denying specific groups of users to ssh + into the system. The list consists of space separated group names. Numeric group IDs are not recognized with this variable. + + + + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + Restricting which users can remotely access the system via SSH will help ensure that only authorized users access the system. + + + + SSHD_CONFIG(5) + SSHD(8) + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + +Edit the /etc/ssh/sshd_config + file to set one or more of the parameters above any Include + and Match + set statements as follows: + +AllowUsers <userlist>
+ - AND/OR -
+AllowGroups <grouplist> +
+ + Note: + + + +First occurrence of a option takes precedence, Match + set statements withstanding. If Include + locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a .conf + file in a Include + directory. + + Be advised + that these options are "ANDed" together. If both AllowUsers + and AllowGroups + are set, connections will be limited to the list of users that are also a member of an allowed group. It is recommended that only one be set for clarity and ease of administration. + It is easier to manage an allow list than a deny list. In a deny list, you could potentially add a user or group and forget to add it to the deny list. + +
+
+
+ + + + + + + + + + +
+ + Ensure sshd Banner is configured + + +The Banner + parameter specifies a file whose contents must be sent to the remote user before authentication is permitted. By default, no banner is displayed. + + + Banners are used to warn connecting users of the particular site's policy regarding connection. Presenting a warning message prior to the normal user login may assist the prosecution of trespassers on the computer system. + + SSHD(8) + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the Banner + parameter above any Include + and Match + entries as follows: + Banner /etc/issue.net + + + Note: + First occurrence of a option takes precedence, Match set statements withstanding. If Include locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include location. + +Edit the file being called by the Banner + argument with the appropriate contents according to your site policy, remove any instances of \m + , \r + , \s + , \v + or references to the OS platform + + + Example: + + # printf '%s\n' "Authorized users only. All activity may be monitored and reported." > "$(sshd -T | awk '$1 == "banner" {print $2}')" + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure sshd ClientAliveInterval and ClientAliveCountMax are configured + + + Note: + To clarify, the two settings described below are only meant for idle connections from a protocol perspective and are not meant to check if the user is active or not. An idle user does not mean an idle connection. SSH does not and never had, intentionally, the capability to drop idle users. In SSH versions before 8.2p1 + there was a bug that caused these values to behave in such a manner that they were abused to disconnect idle users. This bug has been resolved in 8.2p1 + and thus it can no longer be abused disconnect idle users. + +The two options ClientAliveInterval + and ClientAliveCountMax + control the timeout of SSH sessions. Taken directly from man 5 sshd_config +: + + + + ClientAliveInterval + Sets a timeout interval in seconds after which if no data has been received from the client, sshd(8) will send a message through the encrypted channel to request a response from the client. The default is 0, indicating that these messages will not be sent to the client. + + + + ClientAliveCountMax + Sets the number of client alive messages which may be sent without sshd(8) receiving any messages back from the client. If this threshold is reached while client alive messages are being sent, sshd will disconnect the client, terminating the session. It is important to note that the use of client alive messages is very different from TCPKeepAlive. The client alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option en‐abled by TCPKeepAlive is spoofable. The client alive mechanism is valuable when the client or server depend on knowing when a connection has become unresponsive. +The default value is 3. If ClientAliveInterval is set to 15, and ClientAliveCountMax is left at the default, unresponsive SSH clients will be disconnected after approximately 45 seconds. Setting a zero ClientAliveCountMax disables connection termination. + + + + + + + https://bugzilla.redhat.com/show_bug.cgi?id=1873547 + + + https://github.com/openssh/openssh-portable/blob/V_8_9/serverloop.c#L137 + + + + + +In order to prevent resource exhaustion, appropriate values should be set for both ClientAliveInterval + and ClientAliveCountMax +. Specifically, looking at the source code, ClientAliveCountMax + must be greater than zero in order to utilize the ability of SSH to drop idle connections. If connections are allowed to stay open indefinitely, this can potentially be used as a DDOS attack or simple resource exhaustion could occur over unreliable networks. + The example set here is a 45 second timeout. Consult your site policy for network timeouts and apply as appropriate. + + SSHD_CONFIG(5) + SSHD(8) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the ClientAliveInterval + and ClientAliveCountMax + parameters above any Include + and Match + entries according to site policy. + + Example: + + +ClientAliveInterval 15
+ClientAliveCountMax 3 +
+ + Note: + First occurrence of a option takes precedence, Match set statements withstanding. If Include locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include location. +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure sshd DisableForwarding is enabled + + +The DisableForwarding + parameter disables all forwarding features, including X11, ssh-agent(1), TCP and StreamLocal. This option overrides all other forwarding-related options and may simplify restricted configurations. + + X11Forwarding provides the ability to tunnel X11 traffic through the connection to enable remote graphic connections. + ssh-agent is a program to hold private keys used for public key authentication. Through use of environment variables the agent can be located and automatically used for authentication when logging in to other machines using ssh. + SSH port forwarding is a mechanism in SSH for tunneling application ports from the client to the server, or servers to clients. It can be used for adding encryption to legacy applications, going through firewalls, and some system administrators and IT professionals use it for opening backdoors into the internal network from their home machines. + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Disable X11 forwarding unless there is an operational requirement to use X11 applications directly. There is a small risk that the remote X11 servers of users who are logged in via SSH with X11 forwarding could be compromised by other users on the X11 server. Note that even if X11 forwarding is disabled, users can always install their own forwarders. + anyone with root privilege on the the intermediate server can make free use of ssh-agent to authenticate them to other servers + Leaving port forwarding enabled can expose the organization to security risks and backdoors. SSH connections are protected with strong encryption. This makes their contents invisible to most deployed network monitoring and traffic filtering solutions. This invisibility carries considerable risk potential if it is used for malicious purposes such as data exfiltration. Cybercriminals or malware could exploit SSH to hide their unauthorized communications, or to exfiltrate stolen data from the target network. + + + + sshd_config(5) + SSHD(8) + NIST SP 800-53 Rev. 5: CM-7 + + + + +Edit the /etc/ssh/sshd_config + file to set the DisableForwarding + parameter to yes + above any Include + entry as follows: + DisableForwarding yes + + + Note: + First occurrence of a option takes precedence. If Include locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include location. + Impact: + + SSH tunnels are widely used in many corporate environments. In some environments the applications themselves may have very limited native support for security. By utilizing tunneling, compliance with SOX, HIPAA, PCI-DSS, and other standards can be achieved without having to modify the applications. + + + + + + + + + + + + + + + + + Ensure sshd GSSAPIAuthentication is disabled + + +The GSSAPIAuthentication + parameter specifies whether user authentication based on GSSAPI is allowed + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Allowing GSSAPI authentication through SSH exposes the system's GSSAPI to remote hosts, and should be disabled to reduce the attack surface of the system + + + + SSHD_CONFIG(5) + SSHD(8) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the GSSAPIAuthentication + parameter to no + above any Include + and Match + entries as follows: + GSSAPIAuthentication no + + + Note: + First occurrence of an option takes precedence, Match + set statements withstanding. If Include + locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include + location. + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure sshd HostbasedAuthentication is disabled + + +The HostbasedAuthentication + parameter specifies if authentication is allowed through trusted hosts via the user of .rhosts +, or /etc/hosts.equiv +, along with successful public key client host authentication. + + + +Even though the .rhosts + files are ineffective if support is disabled in /etc/pam.conf +, disabling the ability to use .rhosts + files in SSH provides an additional layer of protection. + + SSHD_CONFIG(5) + SSHD(8) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the HostbasedAuthentication + parameter to no + above any Include + and Match + entries as follows: + HostbasedAuthentication no + + + Note: + First occurrence of a option takes precedence, Match + set statements withstanding. If Include + locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include + location. + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure sshd IgnoreRhosts is enabled + + +The IgnoreRhosts + parameter specifies that .rhosts + and .shosts + files will not be used in RhostsRSAAuthentication + or HostbasedAuthentication +. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Setting this parameter forces users to enter a password when authenticating with SSH. + + + + SSHD_CONFIG(5) + SSHD(8) + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the IgnoreRhosts + parameter to yes + above any Include + and Match + entries as follows: + IgnoreRhosts yes + + + Note: + First occurrence of a option takes precedence, Match + set statements withstanding. If Include + locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include + location. + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure sshd LoginGraceTime is configured + + +The LoginGraceTime + parameter specifies the time allowed for successful authentication to the SSH server. The longer the Grace period is the more open unauthenticated connections can exist. Like other session controls in this session the Grace Period should be limited to appropriate organizational limits to ensure the service is available for needed access. + + + +Setting the LoginGraceTime + parameter to a low number will minimize the risk of successful brute force attacks to the SSH server. It will also limit the number of concurrent unauthenticated connections While the recommended setting is 60 seconds (1 Minute), set the number based on site policy. + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-6 + SSHD(8) + + + + +Edit the /etc/ssh/sshd_config + file to set the LoginGraceTime + parameter to 60 + seconds or less above any Include + entry as follows: + LoginGraceTime 60 + + + Note: + First occurrence of a option takes precedence. If Include locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include location. + + + + + + + + + + + + + + + + Ensure sshd LogLevel is configured + + +SSH provides several logging levels with varying amounts of verbosity. The DEBUG + options are specifically not recommended other than strictly for debugging SSH communications. These levels provide so much data that it is difficult to identify important security information, and may violate the privacy of users. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + +The INFO + level is the basic level that only records login activity of SSH users. In many situations, such as Incident Response, it is important to determine when a particular user was active on a system. The logout record can eliminate those users who disconnected, which helps narrow the field. + +The VERBOSE + level specifies that login and logout activity as well as the key fingerprint for any SSH key used for login will be logged. This information is important for SSH key management, especially in legacy environments. + + + + + https://www.ssh.com/ssh/sshd_config/ + NIST SP 800-53 Rev. 5: AU-3, AU-12, SI-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the LogLevel + parameter to VERBOSE + or INFO + above any Include + and Match + entries as follows: + +LogLevel VERBOSE
+ - OR -
+LogLevel INFO +
+ + Note: + First occurrence of an option takes precedence, Match + set statements withstanding. If Include + locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include + location. +
+
+
+ + + + + + + + + + + + + + + + + + + + +
+ + Ensure sshd MaxAuthTries is configured + + +The MaxAuthTries + parameter specifies the maximum number of authentication attempts permitted per connection. When the login failure count reaches half the number, error messages will be written to the syslog + file detailing the login failure. + + + + + + + Network + Detect + + + + + + Users + Detect + + + + + + +Setting the MaxAuthTries + parameter to a low number will minimize the risk of successful brute force attacks to the SSH server. While the recommended setting is 4, set the number based on site policy. + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: AU-3 + + + + +Edit the /etc/ssh/sshd_config + file to set the MaxAuthTries + parameter to 4 + or less above any Include + and Match + entries as follows: + MaxAuthTries 4 + + + Note: + First occurrence of an option takes precedence, Match + set statements withstanding. If Include + locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include + location. + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure sshd MaxStartups is configured + + +The MaxStartups + parameter specifies the maximum number of concurrent unauthenticated connections to the SSH daemon. + + + To protect a system from denial of service due to a large number of pending authentication connection attempts, use the rate limiting function of MaxStartups to protect availability of sshd logins and prevent overwhelming the daemon. + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the MaxStartups + parameter to 10:30:60 + or more restrictive above any Include + entries as follows: + MaxStartups 10:30:60 + + + Note: + First occurrence of a option takes precedence. If Include locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include location. + + + + + + + + + + + + + + + + Ensure sshd MaxSessions is configured + + +The MaxSessions + parameter specifies the maximum number of open sessions permitted from a given connection. + + + To protect a system from denial of service due to a large number of concurrent sessions, use the rate limiting function of MaxSessions to protect availability of sshd logins and prevent overwhelming the daemon. + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the MaxSessions + parameter to 10 + or less above any Include + and Match + entries as follows: + MaxSessions 10 + + + Note: + First occurrence of an option takes precedence, Match + set statements withstanding. If Include + locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include + location. + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure sshd PermitEmptyPasswords is disabled + + +The PermitEmptyPasswords + parameter specifies if the SSH server allows login to accounts with empty password strings. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Disallowing remote shell access to accounts that have an empty password reduces the probability of unauthorized access to the system. + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Edit /etc/ssh/sshd_config + and set the PermitEmptyPasswords + parameter to no + above any Include + and Match + entries as follows: + PermitEmptyPasswords no + + + Note: + First occurrence of an option takes precedence, Match + set statements withstanding. If Include + locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include + location. + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure sshd PermitRootLogin is disabled + + +The PermitRootLogin + parameter specifies if the root user can log in using SSH. The default is prohibit-password +. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + +Disallowing root + logins over SSH requires system admins to authenticate using their own individual account, then escalating to root +. This limits opportunity for non-repudiation and provides a clear audit trail in the event of a security incident. + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5:AC-6 + + + + +Edit the /etc/ssh/sshd_config + file to set the PermitRootLogin + parameter to no + above any Include + and Match + entries as follows: + PermitRootLogin no + + + Note: + First occurrence of an option takes precedence, Match + set statements withstanding. If Include + locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include + location. + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure sshd PermitUserEnvironment is disabled + + +The PermitUserEnvironment + option allows users to present environment options to the SSH daemon. + + + Permitting users the ability to set environment variables through the SSH daemon could potentially allow users to bypass security controls (e.g. setting an execution path that has SSH executing trojan'd programs) + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + SSHD(8) + + + + +Edit the /etc/ssh/sshd_config + file to set the PermitUserEnvironment + parameter to no + above any Include + entries as follows: + PermitUserEnvironment no + + + Note: + First occurrence of an option takes precedence. If Include locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include location. + + + + + + + + + + + + + + + + Ensure sshd UsePAM is enabled + + +The UsePAM + directive enables the Pluggable Authentication Module (PAM) interface. If set to yes + this will enable PAM authentication using ChallengeResponseAuthentication + and PasswordAuthentication + directives in addition to PAM account and session module processing for all authentication types. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + +When usePAM + is set to yes +, PAM runs through account and session types properly. This is important if you want to restrict access to services based off of IP, time or other factors of the account. Additionally, you can make sure users inherit certain environment variables on login or disallow access to the server + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + SSHD(8) + + + + +Edit the /etc/ssh/sshd_config + file to set the UsePAM + parameter to yes + above any Include + entries as follows: + UsePAM yes + + + Note: + First occurrence of an option takes precedence. If Include locations are enabled, used, and order of precedence is understood in your environment, the entry may be created in a file in Include location. + + + + + + + + + + + + + + +
+ + Configure privilege escalation + + There are various tools which allows a permitted user to execute a command as the superuser or another user, as specified by the security policy. + + sudo + + + https://www.sudo.ws/ + + The invoking user's real (not effective) user ID is used to determine the user name with which to query the security policy. + + sudo + supports a plug-in architecture for security policies and input/output logging. Third parties can develop and distribute their own policy and I/O logging plug-ins to work seamlessly with the sudo + front end. The default security policy is sudoers +, which is configured via the file /etc/sudoers + and any entries in /etc/sudoers.d +. + + pkexec + + + https://www.freedesktop.org/software/polkit/docs/0.105/pkexec.1.html + + + + Ensure sudo is installed + + + sudo + allows a permitted user to execute a command as the superuser or another user, as specified by the security policy. The invoking user's real (not effective) user ID is used to determine the user name with which to query the security policy. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + + sudo + supports a plug-in architecture for security policies and input/output logging. Third parties can develop and distribute their own policy and I/O logging plug-ins to work seamlessly with the sudo + front end. The default security policy is sudoers +, which is configured via the file /etc/sudoers + and any entries in /etc/sudoers.d +. + +The security policy determines what privileges, if any, a user has to run sudo +. The policy may require that users authenticate themselves with a password or another authentication mechanism. If authentication is required, sudo + will exit if the user's password is not entered within a configurable time limit. This limit is policy-specific. + + + + SUDO(8) + NIST SP 800-53 Rev. 5: AC-6(2), AC-6(5) + + + + Run the following command to install sudo + # dnf install sudo + + + + + + + + + + + + Ensure sudo commands use pty + + + sudo + can be configured to run only from a pseudo terminal ( pseudo-pty +). + + + + + + + Users + Protect + + + + + + Applications + Protect + + + + + + +Attackers can run a malicious program using sudo + which would fork a background process that remains even when the main program has finished executing. + + + + SUDO(8) + VISUDO(8) + sudoers(5) + NIST SP 800-53 Rev. 5: AC-6 + + + + +Edit the file /etc/sudoers + with visudo + or a file in /etc/sudoers.d/ + with visudo -f <PATH TO FILE> + and add the following line: + Defaults use_pty + + +Edit the file /etc/sudoers + with visudo + and any files in /etc/sudoers.d/ + with visudo -f <PATH TO FILE> + and remove any occurrence of !use_pty + + + Note: + + + +sudo will read each file in /etc/sudoers.d +, skipping file names that end in ~ + or contain a . + character to avoid causing problems with package manager or editor temporary/backup files. + +Files are parsed in sorted lexical order. That is, /etc/sudoers.d/01_first + will be parsed before /etc/sudoers.d/10_second +. + +Be aware that because the sorting is lexical, not numeric, /etc/sudoers.d/1_whoops + would be loaded after /etc/sudoers.d/10_second +. + Using a consistent number of leading zeroes in the file names can be used to avoid such problems. + + Impact: + + + WARNING: + Editing the sudo + configuration incorrectly can cause sudo + to stop functioning. Always use visudo + to modify sudo + configuration files. + + + + + + + + + + + + + + + + + + + + + + + + + Ensure sudo log file exists + + +The Defaults logfile + entry sets the path to the sudo log file. Setting a path turns on logging to a file; negating this option turns it off. By default, sudo logs via syslog. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + visudo edits the sudoers file in a safe fashion, analogous to vipw(8). visudo locks the sudoers file against multiple simultaneous edits, provides basic sanity checks, and checks for parse errors. If the sudoers file is currently being edited you will receive a message to try again later. + + + + Defining a dedicated log file for sudo simplifies auditing of sudo commands and creation of auditd rules for sudo. + + + + SUDO(8) + VISUDO(8) + sudoers(5) + NIST SP 800-53 Rev. 5: AU-3, AU-12 + + + + +Edit the file /etc/sudoers + or a file in /etc/sudoers.d/ + with visudo -f <PATH TO FILE> + and add the following line: + Defaults logfile="<PATH TO CUSTOM LOG FILE>" + + + Example + + Defaults logfile="/var/log/sudo.log" + + + Notes: + + + +sudo will read each file in /etc/sudoers.d +, skipping file names that end in ~ + or contain a . + character to avoid causing problems with package manager or editor temporary/backup files. + +Files are parsed in sorted lexical order. That is, /etc/sudoers.d/01_first + will be parsed before /etc/sudoers.d/10_second +. + +Be aware that because the sorting is lexical, not numeric, /etc/sudoers.d/1_whoops + would be loaded after /etc/sudoers.d/10_second +. + Using a consistent number of leading zeroes in the file names can be used to avoid such problems. + + Impact: + + + WARNING: + Editing the sudo + configuration incorrectly can cause sudo + to stop functioning. Always use visudo + to modify sudo + configuration files. + +Creation of additional log files can cause disk space exhaustion if not correctly managed. You should configure logrotate + to manage the sudo log in accordance with your local policy. + + + + + + + + + + + + + + + Ensure users must provide password for escalation + + The operating system must be configured so that users must provide a password for privilege escalation. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Without re-authentication, users may access resources or perform tasks for which they do not have authorization. + When operating systems provide the capability to escalate a functional capability, it is critical the user re-authenticate. + + + + NIST SP 800-53 Rev. 5: AC-6 + + + + +Based on the outcome of the audit procedure, use visudo -f <PATH TO FILE> + to edit the relevant sudoers file. + +Remove any line with occurrences of NOPASSWD + tags in the file. + Impact: + + This will prevent automated processes from being able to elevate privileges. + + + + + + + + + + + + + + + Ensure re-authentication for privilege escalation is not disabled globally + + The operating system must be configured so that users must re-authenticate for privilege escalation. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Without re-authentication, users may access resources or perform tasks for which they do not have authorization. + When operating systems provide the capability to escalate a functional capability, it is critical the user re-authenticate. + + + + NIST SP 800-53 Rev. 5: AC-6 + + + + Configure the operating system to require users to reauthenticate for privilege escalation. + +Based on the outcome of the audit procedure, use visudo -f <PATH TO FILE> + to edit the relevant sudoers file. + +Remove any occurrences of !authenticate + tags in the file(s). + + + + + + + + + + + + + + Ensure sudo authentication timeout is configured correctly + + + sudo + caches used credentials for a default of 5 minutes. This is for ease of use when there are multiple administrative tasks to perform. The timeout can be modified to suit local security policies. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Setting a timeout value reduces the window of opportunity for unauthorized privileged access to another user. + + + + https://www.sudo.ws/man/1.9.0/sudoers.man.html + + + + +If the currently configured timeout is larger than 15 minutes, edit the file listed in the audit section with visudo -f <PATH TO FILE> + and modify the entry timestamp_timeout= + to 15 minutes or less as per your site policy. The value is in minutes. This particular entry may appear on its own, or on the same line as env_reset +. See the following two examples: + Defaults env_reset, timestamp_timeout=15 + + +Defaults timestamp_timeout=15
+Defaults env_reset +
+
+
+
+ + + + + + + + +
+ + Ensure access to the su command is restricted + + +The su + command allows a user to run a command or shell as another user. The program has been superseded by sudo +, which allows for more granular control over privileged access. Normally, the su + command can be executed by any user. By uncommenting the pam_wheel.so + statement in /etc/pam.d/su +, the su + command will only allow users in a specific groups to execute su +. This group should be empty to reinforce the use of sudo + for privileged access. + + + + + + + Data + Protect + + + + + + Applications + Protect + + + + + + +Restricting the use of su + , and using sudo + in its place, provides system administrators better control of the escalation of user privileges to execute privileged commands. The sudo utility also provides a better logging and audit mechanism, as it can log each command executed via sudo + , whereas su + can only record that a user executed the su + program. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Create an empty group that will be specified for use of the su + command. The group should be named according to site policy. + + Example: + + # groupadd sugroup + + +Add the following line to the /etc/pam.d/su + file, specifying the empty group: + auth required pam_wheel.so use_uid group=sugroup + + + + + + + + + + +
+ + Pluggable Authentication Modules + + +Pluggable Authentication Modules (PAM) is a service that implements modular authentication modules on UNIX systems. PAM is implemented as a set of shared objects that are loaded and executed when a program needs to authenticate a user. Files for PAM are typically located in the /etc/pam.d + directory. PAM must be carefully configured to secure system authentication. While this section covers some of PAM, please consult other PAM resources to fully understand the configuration capabilities. + + + Configure PAM software packages + + Updated versions of PAM and authselect include additional functionality + + + Ensure latest version of pam is installed + + Updated versions of PAM include additional functionality + + + To ensure the system has full functionality and access to the options covered by this Benchmark, pam-1.5.1-19 or latter is required + + + + + + - IF - + the version of PAM + on the system is less that version pam-1.5.1-19 +: + +Run the following command to update to the latest version of PAM +: + # dnf upgrade pam + + + + + + + + + + + + + + Ensure latest version of authselect is installed + + Authselect is a utility that simplifies the configuration of user authentication. Authselect offers ready-made profiles that can be universally used with all modern identity management systems + You can create and deploy a custom profile by customizing one of the default profiles, the sssd, winbind, or the nis profile. This is particularly useful if Modifying a ready-made authselect profile is not enough for your needs. When you deploy a custom profile, the profile is applied to every user logging into the given host. This would be the recommended method, so that the existing profiles can remain unmodified. + +Updated versions of authselect + include additional functionality + + + Authselect makes testing and troubleshooting easy because it only modifies files in these directories: + + + /etc/nsswitch.conf + + + /etc/pam.d/* + + + /etc/dconf/db/distro.d/* + + + +To ensure the system has full functionality and access to the options covered by this Benchmark, authselect-1.2.6-2 + or latter is required + + + + + +Run the following command to install authselect +: + # dnf install authselect + + + - IF - + the version of authselect + on the system is less that version authselect-1.2.6-2 +: + +Run the following command to update to the latest version of authselect +: + # dnf upgrade authselect + + Impact: + + +If local site customizations have been made to an authselect default or custom profile created with the --symlink-pam + option, these customizations may be over-written by updating authselect. + + WARNING: + + Do not use authselect if: + + your host is part of Linux Identity Management. Joining your host to an IdM domain with the ipa-client-install command automatically configures SSSD authentication on your host. + Your host is part of Active Directory via SSSD. Calling the realm join command to join your host to an Active Directory domain automatically configures SSSD authentication on your host. + + +It is not + recommended to change the authselect profiles configured by ipa-client-install or realm join. If you need to modify them, display the current settings before making any modifications, so you can revert back to them if necessary + + + + + + + + + + + + + + Ensure latest version of libpwquality is installed + + + libpwquality + provides common functions for password quality checking and scoring them based on their apparent randomness. The library also provides a function for generating random passwords with good pronounceability. + +This module can be plugged into the password stack of a given service to provide some plug-in strength-checking for passwords. The code was originally based on pam_cracklib + module and the module is backwards compatible with its options. + + + Strong passwords reduce the risk of systems being hacked through brute force methods. + + + + + +Run the following command to install libpwquality +: + # dnf install libpwquality + + + - IF - + the version of libpwquality + on the system is less that version libpwquality-1.4.4-8 +: + +Run the following command to update to the latest version of libpwquality +: + # dnf upgrade libpwquality + + + + + + + + + + + + + + + Configure authselect + + Authselect is a utility that simplifies the configuration of user authentication. Authselect offers ready-made profiles that can be universally used with all modern identity management systems + Authselect makes testing and troubleshooting easy because it only modifies files in these directories: + + + /etc/nsswitch.conf + + + /etc/pam.d/* files + + + /etc/dconf/db/distro.d/* files + + + You can create and deploy a custom profile by customizing one of the default profiles, the sssd, winbind, or the nis profile. This is particularly useful if Modifying a ready-made authselect profile is not enough for your needs. When you deploy a custom profile, the profile is applied to every user logging into the given host. This would be the recommended method, so that the existing profiles can remain unmodified. + Profile Directories - Profiles can be found in one of three directories: + + + /usr/share/authselect/default + - Read-only directory containing profiles shipped together with authselect. + + /usr/share/authselect/vendor + - Read-only directory for vendor-specific profiles that can override the ones in default directory. + + /etc/authselect/custom + - Place for administrator-defined profiles. + + Profile Files - Each profile consists of one or more of these files which provide a mandatory profile description and describe the changes that are done to the system: + + + README + - Description of the profile. The first line must be a name of the profile. + + system-auth + - PAM stack that is included from nearly all individual service configuration files. + + password-auth +, smartcard-auth +, fingerprint-auth + - These PAM stacks are for applications which handle authentication from different types of devices via simultaneously running individual conversations instead of one aggregate conversation. + + postlogin + - The purpose of this PAM stack is to provide a common place for all PAM modules which should be called after the stack configured in system-auth + or the other common PAM configuration files. It is included from all individual service configuration files that provide login service with shell or file access. Note: + the modules in the postlogin configuration file are executed regardless of the success or failure of the modules in the system-auth + configuration file. + + nsswitch.conf + - Name Service Switch configuration file. Only maps relevant to the profile must be set. Maps that are not specified by the profile are included from /etc/authselect/user-nsswitch.conf +. + + dconf-db + - Changes to dconf database. The main uses case of this file is to set changes for gnome login screen in order to enable or disable smartcard and fingerprint authentication. + + dconf-locks + - This file define locks on values set in dconf database. + + Conditional lines - Each of these files serves as a template. A template is a plain text file with optional usage of several operators that can be used to provide some optional profile features. + + + {continue if "feature"} + - Immediately stop processing of the file unless "feature" is defined (the rest of the file content will be removed). If "feature" is defined, the whole line with this operator will be removed and the rest of the template will be processed. + + {stop if "feature"} + - Opposite of "continue if". Immediately stop processing of the file if "feature" is defined (the rest of the file content will be removed). If "feature" is not defined, the whole line with this operator will be removed and the rest of the template will be processed. + + {include if "feature"} + - Include the line where this operator is placed only if "feature" is defined. + + {exclude if "feature"} + - Opposite to "include-if". Include the line where this operator is placed only if "feature" is not defined. + + {imply "implied-feature" if "feature"} + - Enable feature "implied-feature" if feature "feature" is enabled. The whole line with this operator is removed, thus it is not possible to add anything else around this operator at the same line. + + {if "feature":true|false} + - If "feature" is defined, replace this operator with string "true", otherwise with string "false". + + {if "feature":true} + - If "feature" is defined, replace this operator with string "true", otherwise with an empty string. + + + Example of creating a custom authselect profile called custom-profile + + # authselect create-profile custom-profile -b sssd + + + WARNING: + + Do not use authselect if: + + your host is part of Linux Identity Management. Joining your host to an IdM domain with the ipa-client-install command automatically configures SSSD authentication on your host. + Your host is part of Active Directory via SSSD. Calling the realm join command to join your host to an Active Directory domain automatically configures SSSD authentication on your host. + It is not recommended to change the authselect profiles configured by ipa-client-install or realm join. If you need to modify them, display the current settings before making any modifications, so you can revert back to them if necessary + + + + Ensure active authselect profile includes pam modules + + A custom profile can be created by copying and customizing one of the default profiles. The default profiles include: sssd, winbind, or the nis. This profile can then be customized to follow site specific requirements. + You can select a profile for the authselect utility for a specific host. The profile will be applied to every user logging into the host. + + + + + + + Applications + Protect + + + + + + Users + Protect + + + + + +with the option --base-on=BASE-ID + or -b=BASE-ID + the new profile will be based on a profile named BASE-ID. + The base profile location is determined with these steps: + + If BASE-ID starts with prefix custom/ it is a custom profile. + Try if BASE-ID is found in vendor profiles. + Try if BASE-ID is found in default profiles. + Return an error. + + +The authselect option --force + or -f + will cause authselect to write changes even if the previous configuration was not created by authselect but by other tool or by manual changes. This option will automatically backup system files before writing any change unless the --nobackup + option is set. + + Example: + + authselect select custom/custom-profile with-pwhistory with-faillock without-nullok --backup=PAM_CONFIG_BACKUP --force + + + + + A custom profile is required to customize many of the pam options. + Modifications made to a default profile may be overwritten during an update. + When you deploy a profile, the profile is applied to every user logging into the given host + + + + authselect(8) + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html/configuring_authentication_and_authorization_in_rhel/configuring-user-authentication-using-authselect_configuring-authentication-and-authorization-in-rhel#creating-and-deploying-your-own-authselect-profile_configuring-user-authentication-using-authselect + https://docs.oracle.com/en/operating-systems/oracle-linux/9/userauth/OL9-USERAUTH.pdf + + + + Perform the following to create a custom authselect profile, with the modules covered in this Benchmark correctly included in the custom profile template files + Run the following command to create a custom authselect profile: + # authselect create-profile <custom-profile name> <options> + + + Example: + + # authselect create-profile custom-profile -b sssd + + Run the following command to select a custom authselect profile: + # authselect select custom/<CUSTOM PROFILE NAME> {with-<OPTIONS>} {--force} + + + Example: + + # authselect select custom/custom-profile --backup=PAM_CONFIG_BACKUP --force + + + Notes: + + + +The PAM and authselect packages must be versions pam-1.3.1-25 + and authselect-1.2.6-1 + or newer + +The example is based on a custom profile built (copied) from the the SSSD + default authselect profile. + +The example does not include the symlink + option for the PAM + or Metadata + files. This is due to the fact that by linking the PAM + files future updates to authselect + may overwrite local site customizations to the custom profile + +The --backup=PAM_CONFIG_BACKUP + option will create a backup of the current config. The backup will be stored at /var/lib/authselect/backups/PAM_CONFIG_BACKUP + + +The --force + option will force the overwrite of the existing files and automatically backup system files before writing any change unless the --nobackup + option is set. + + +On a new system where authselect has not been configured. In this case, the --force + option will force the selected authselect profile to be active and overwrite the existing files with files generated from the selected authselect profile's templates + +On an existing system with a custom configuration. The --force + option may be used, but ensure that you note the backup location included as your custom files will be overwritten. + This will allow you to review the changes and add any necessary customizations to the template files for the authselect profile. After updating the templates, run the command authselect apply-changes + to add these custom entries to the files in /etc/pam.d/ + + + + + + - IF - + you receive an error ending with a message similar to: + +[error] Refusing to activate profile unless those changes are removed or overwrite is requested.
+Some unexpected changes to the configuration were detected. Use 'select' command instead. +
+ +This error is caused when the previous configuration was not created by authselect but by other tool or by manual changes and the --force + option will be required to enable the authselect profile. + Impact: + + +If local site customizations have been made to the authselect template or files in /etc/pam.d + these custom entries should be added to the newly created custom profile before it's applied to the system. + + Note: + The order within the pam stacks is important when adding these entries. Specifically for the password stack, the use_authtok + option is important, and should appear on all modules except for the first entry. + + Example: + + +password requisite pam_pwquality.so local_users_only #<-- Top of password stack, doesn't include use_authtok
+password required pam_pwhistory.so use_authtok #<-- subsequent entry in password stack, includes use_authtok +
+
+
+
+
+ + + + + + +
+ + Ensure pam_faillock module is enabled + + +The pam_faillock.so + module maintains a list of failed authentication attempts per user during a specified interval and locks the account in case there were more than the configured number of consecutive failed authentications (this is defined by the deny + parameter in the faillock configuration). It stores the failure records into per-user files in the tally directory. + + + + + + + Applications + Protect + + + + + + Users + Protect + + + + + + Locking out user IDs after n unsuccessful consecutive login attempts mitigates brute force password attacks against your systems. + + + + faillock(8) - Linux man page + pam_faillock(8) - Linux man page + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Run the following script to verify the pam_faillock.so + lines exist in the profile templates: + +#!/usr/bin/env bash
+
+{
+ l_module_name="faillock"
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ grep -P -- "\bpam_$l_module_name\.so\b" "$l_pam_profile_path"/{password,system}-auth
+} +
+ + Example Output with a custom profile named "custom-profile": + + +/etc/authselect/custom/custom-profile/password-auth:auth required pam_faillock.so preauth silent {include if "with-faillock"}
+/etc/authselect/custom/custom-profile/password-auth:auth required pam_faillock.so authfail {include if "with-faillock"}
+/etc/authselect/custom/custom-profile/password-auth:account required pam_faillock.so {include if "with-faillock"}
+
+/etc/authselect/custom/custom-profile/system-auth:auth required pam_faillock.so preauth silent {include if "with-faillock"}
+/etc/authselect/custom/custom-profile/system-auth:auth required pam_faillock.so authfail {include if "with-faillock"}
+/etc/authselect/custom/custom-profile/system-auth:account required pam_faillock.so {include if "with-faillock"} +
+ + Note: + The lines may not include {include if "with-faillock"} + + + - IF - + the lines shown above are not returned, refer to the Recommendation "Ensure active authselect profile includes pam modules" to update the authselect profile template files to include the pam_faillock + entries before continuing this remediation. + + - IF - + the lines include {include if "with-faillock"} +, run the following command to enable the authselect with-faillock + feature and update the files in /etc/pam.d + to include pam_faillock.so +: + # authselect enable-feature with-faillock + + + - IF - + any of the pam_faillock + lines exist without {include if "with-faillock"} +, run the following command to update the files in /etc/pam.d + to include pam_faillock.so +: + # authselect apply-changes + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure pam_pwquality module is enabled + + +The pam_pwquality.so + module performs password quality checking. This module can be plugged into the password stack of a given service to provide strength-checking for passwords. The code was originally based on pam_cracklib module and the module is backwards compatible with its options. + The action of this module is to prompt the user for a password and check its strength against a system dictionary and a set of rules for identifying poor choices. + The first action is to prompt for a single password, check its strength and then, if it is considered strong, prompt for the password a second time (to verify that it was typed correctly on the first occasion). All being well, the password is passed on to subsequent modules to be installed as the new authentication token. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Use of a unique, complex passwords helps to increase the time and resources required to compromise the password. + + + + NIST SP 800-53 Rev. 5: IA-5(1) + + + + Review the authselect profile templates: + +Run the following script to verify the pam_pwquality.so + lines exist in the active profile templates: + +#!/usr/bin/env bash
+
+{
+ l_module_name="pwquality"
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ grep -P -- "\bpam_$l_module_name\.so\b" "$l_pam_profile_path"/{password,system}-auth
+} +
+ + Example Output with a custom profile named "custom-profile": + + +/etc/authselect/custom/custom-profile/password-auth:password requisite pam_pwquality.so local_users_only {include if "with-pwquality"}
+
+/etc/authselect/custom/custom-profile/system-auth:password requisite pam_pwquality.so local_users_only {include if "with-pwquality"} +
+ + Note: + The lines may not include {include if "with-pwquality"} + + + - IF - + the lines shown above are not returned, refer to the Recommendation "Ensure active authselect profile includes pam modules" to update the authselect profile template files to include the pam_pwquality + entries before continuing this remediation. + + - IF - + any of the pam_pwquality + lines include {include if "with-pwquality"} +, run the following command to enable the authselect with-pwquality + feature and update the files in /etc/pam.d + to include `pam_pwquality: + # authselect enable-feature with-pwquality + + + - IF - + any of the pam_pwquality + lines exist without {include if "with-pwquality"} +, run the following command to update the files in /etc/pam.d + to include pam_pwquality.so +: + # authselect apply-changes + +
+
+
+ + + + + + + + +
+ + Ensure pam_pwhistory module is enabled + + +The pam_history.so + module saves the last passwords for each user in order to force password change history and keep the user from alternating between the same password too frequently. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Requiring users not to reuse their passwords make it less likely that an attacker will be able to guess the password or use a compromised password. + + + + NIST SP 800-53 Rev. 5: IA-5(1) + + + + +Run the following script to verify the pam_pwhistory.so + lines exist in the profile templates: + +#!/usr/bin/env bash
+
+{
+ l_module_name="pwhistory"
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ grep -P -- "\bpam_$l_module_name\.so\b" "$l_pam_profile_path"/{password,system}-auth
+} +
+ + Example Output with a custom profile named "custom-profile": + + +/etc/authselect/custom/custom-profile/password-auth:password required pam_pwhistory.so use_authtok {include if "with-pwhistory"}
+
+/etc/authselect/custom/custom-profile/system-auth:password required pam_pwhistory.so use_authtok {include if "with-pwhistory"} +
+ + Note: + The lines may not include {include if "with-pwhistory"} + + + - IF - + the lines shown above are not returned, refer to the Recommendation "Ensure active authselect profile includes pam modules" to update the authselect profile template files to include the pam_pwhistory + entries before continuing this remediation. + + - IF - + the lines include {include if "with-pwhistory"} +, run the following command to enable the authselect with-pwhistory + feature and update the files in /etc/pam.d + to include pam_faillock.so +: + # authselect enable-feature with-pwhistory + + + - IF - + any of the pam_pwhistory + lines exist without {include if "with-pwhistory"} +, run the following command to update the files in /etc/pam.d + to include pam_pwhistory.so +: + # authselect apply-changes + +
+
+
+ + + + + + + + +
+ + Ensure pam_unix module is enabled + + +The pam_unix.so + module is the standard Unix authentication module. It uses standard calls from the system's libraries to retrieve and set account information as well as authentication. Usually this is obtained from the /etc/passwd + and the /etc/shadow + file as well if shadow is enabled. + + + Requiring users to use authentication make it less likely that an attacker will be able to access the system. + + NIST SP 800-53 Rev. 5: IA-5(1) + + + + +Run the following script to verify the pam_unix.so + lines exist in the profile templates: + +#!/usr/bin/env bash
+
+{
+ l_module_name="unix"
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ grep -P -- "\bpam_$l_module_name\.so\b" "$l_pam_profile_path"/{password,system}-auth
+} +
+ + Example Output with a custom profile named "custom-profile": + + +/etc/authselect/custom/custom-profile/password-auth:auth sufficient pam_unix.so {if not "without-nullok":nullok}
+/etc/authselect/custom/custom-profile/password-auth:account required pam_unix.so
+/etc/authselect/custom/custom-profile/password-auth:password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok remember=5
+/etc/authselect/custom/custom-profile/password-auth:session required pam_unix.so
+
+/etc/authselect/custom/custom-profile/system-auth:auth sufficient pam_unix.so {if not "without-nullok":nullok}
+/etc/authselect/custom/custom-profile/system-auth:account required pam_unix.so
+/etc/authselect/custom/custom-profile/system-auth:password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok
+/etc/authselect/custom/custom-profile/system-auth:session required pam_unix.so +
+ + - IF - + the lines shown above are not returned, refer to the Recommendation "Ensure active authselect profile includes pam modules" to update the authselect profile template files to include the pam_unix + entries before continuing this remediation. + + Note: + Arguments following pam_unix.so + may be different than the example output +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + Configure PAM Arguments + + Pluggable Authentication Modules (PAM) uses arguments to pass information to a pluggable module during authentication for a particular module type. These arguments allow the PAM configuration files for particular programs to use a common PAM module but in different ways. + +Invalid arguments are ignored and do not otherwise affect the success or failure of the PAM module. When an invalid argument is passed, an error is usually written to /var/log/messages + file. However, since the reporting method is controlled by the PAM module, the module must be written correctly to log the error to this file. + + + Configure pam_faillock module + + faillock.conf provides a way to configure the default settings for locking the user after multiple failed authentication attempts. This file is read by the pam_faillock module and is the preferred method over configuring pam_faillock directly. + +The file has a very simple name = value format with possible comments starting with # character. The whitespace at the beginning of line, end of line, and around the = + sign is ignored. + Options: + + + <dir=/path/to/tally-directory> + - The directory where the user files with the failure records are kept. The default is /var/run/faillock. Note: These files will disappear after reboot on systems configured with directory /var/run/faillock mounted on virtual memory. + + audit + - Will log the user name into the system log if the user is not found. + + silent + - Don't print informative messages to the user. Please note that when this option is not used there will be difference in the authentication behavior for users which exist on the system and non-existing users. + + no_log_info + - Don't log informative messages via syslog(3). + + local_users_only + - Only track failed user authentications attempts for local users in /etc/passwd and ignore centralized (AD, IdM, LDAP, etc.) users. The faillock(8) command will also no longer track user failed authentication attempts. Enabling this option will prevent a double-lockout scenario where a user is locked out locally and in the centralized mechanism. + + nodelay + - Don't enforce a delay after authentication failures. + + deny=<n> + - Deny access if the number of consecutive authentication failures for this user during the recent interval exceeds +. The default is 3. + + fail_interval=n + - The length of the interval during which the consecutive authentication failures must happen for the user account lock out is n seconds. The default is 900 (15 minutes). + + unlock_time=n + - The access will be re-enabled after n seconds after the lock out. The value 0 has the same meaning as value never - the access will not be re-enabled without resetting the faillock entries by the faillock(8) command. The default is 600 (10 minutes). Note that the default directory that pam_faillock uses is usually cleared on system boot so the access will be also re-enabled after system reboot. If that is undesirable a different tally directory must be set with the dir option. Also note that it is usually undesirable to permanently lock out users as they can become easily a target of denial of service attack unless the usernames are random and kept secret to potential attackers. + + even_deny_root + - Root account can become locked as well as regular accounts. + + root_unlock_time=n + - This option implies even_deny_root option. Allow access after n seconds to root account after the account is locked. In case the option is not specified the value is the same as of the unlock_time option. + + admin_group=name + - If a group name is specified with this option, members of the group will be handled by this module the same as the root account (the options even_deny_root and root_unlock_time will apply to them. By default the option is not set. + + + +Example /etc/security/faillock.conf + file: + + + deny=5
+ unlock_time=900
+ even_deny_root +
+
+ + Ensure password failed attempts lockout is configured + + +The deny=<n> + option will deny access if the number of consecutive authentication failures for this user during the recent interval exceeds +. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + +If a user has been locked out because they have reached the maximum consecutive failure count defined by deny= + in the pam_faillock.so + module, the user can be unlocked by issuing the command faillock --user &amp;lt;USERNAME&amp;amp;gt; --reset +. This command sets the failed count to 0, effectively unlocking the user. + + + + +Locking out user IDs after n + unsuccessful consecutive login attempts mitigates brute force password attacks against your systems. + + + + + + + +Create or edit the following line in /etc/security/faillock.conf + setting the deny + option to 5 + or less: + deny = 5 + + +Run the following script to remove the deny + argument from the pam_faillock.so + module in the PAM files: + +#!/usr/bin/env bash
+{
+ for l_pam_file in system-auth password-auth; do
+ l_authselect_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*auth\s+(requisite|required|sufficient)\s+pam_faillock\.so.*)(\s+deny\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ done
+ authselect apply-changes
+} +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure password unlock time is configured + + + unlock_time=<n> + - The access will be re-enabled after + seconds after the lock out. The value 0 + has the same meaning as value never - the access will not be re-enabled without resetting the faillock entries by the faillock(8) command. + + Notes: + + + The default directory that pam_faillock uses is usually cleared on system boot so the access will be also re-enabled after system reboot. If that is undesirable a different tally directory must be set with the dir option. + It is usually undesirable to permanently lock out users as they can become easily a target of denial of service attack unless the usernames are random and kept secret to potential attackers. + +The maximum configurable value for unlock_time + is 604800 + + + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + +If a user has been locked out because they have reached the maximum consecutive failure count defined by deny= + in the pam_faillock.so + module, the user can be unlocked by issuing the command faillock --user &amp;lt;USERNAME&amp;amp;gt; --reset +. This command sets the failed count to 0, effectively unlocking the user. + + + + +Locking out user IDs after n + unsuccessful consecutive login attempts mitigates brute force password attacks against your systems. + + + + + + + +Set password unlock time to conform to site policy. unlock_time + should be 0 + (never), or 900 + seconds or greater. + +Edit /etc/security/faillock.conf + and update or add the following line: + unlock_time = 900 + + +Run the following script to remove the unlock_time + argument from the pam_faillock.so + module in the PAM files: + +#!/usr/bin/env bash
+{
+ for l_pam_file in system-auth password-auth; do
+ l_authselect_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*auth\s+(requisite|required|sufficient)\s+pam_faillock\.so.*)(\s+unlock_time\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ done
+ authselect apply-changes
+}
+`` +
+ Impact: + + +Use of unlock_time=0 + may allow an attacker to cause denial of service to legitimate users. This will also require a systems administrator with elevated privileges to unlock the account. + +
+
+
+ + + + + + + + + + + + + +
+ + Ensure password failed attempts lockout includes root account + + + even_deny_root + - Root account can become locked as well as regular accounts + + root_unlock_time=n + - This option implies even_deny_root option. Allow access after n seconds to root account after the account is locked. In case the option is not specified the value is the same as of the unlock_time option. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + +If a user has been locked out because they have reached the maximum consecutive failure count defined by deny= + in the pam_faillock.so + module, the user can be unlocked by issuing the command faillock --user &amp;lt;USERNAME&amp;amp;gt; --reset +. This command sets the failed count to 0, effectively unlocking the user. + + + + Locking out user IDs after n unsuccessful consecutive login attempts mitigates brute force password attacks against your systems. + + + + + + + +Edit /etc/security/faillock.conf +: + + +Remove or update any line containing root_unlock_time +, - OR - + set it to a value of 60 + or more + Update or add the following line: + + even_deny_root + + +Run the following script to remove the even_deny_root + and root_unlock_time + arguments from the pam_faillock.so + module in the PAM files: + +#!/usr/bin/env bash
+{
+ for l_pam_file in system-auth password-auth; do
+ l_authselect_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*auth\s+(.*)\s+pam_faillock\.so.*)(\s+even_deny_root)(.*$)/\1\4/' "$l_authselect_file"
+ sed -ri 's/(^\s*auth\s+(.*)\s+pam_faillock\.so.*)(\s+root_unlock_time\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ done
+ authselect apply-changes
+}
+`` +
+ Impact: + + +Use of unlock_time=0 + or root_unlock_time=0 + may allow an attacker to cause denial of service to legitimate users. + +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ + Configure pam_pwquality module + + +The pam_pwquality.so + module checks the strength of passwords. It performs checks such as making sure a password is not a dictionary word, it is a certain length, contains a mix of characters (e.g. alphabet, numeric, other) and more. + These checks are configurable by either: + + use of the module arguments + +modifying the /etc/security/pwquality.conf + configuration file + +creating a .conf + file in the /etc/security/pwquality.conf.d/ + directory. + + + Note: + The module arguments override the settings in the /etc/security/pwquality.conf + configuration file. Settings in the /etc/security/pwquality.conf + configuration file override settings in a .conf + file in the /etc/security/pwquality.conf.d/ + directory. + + + Ensure password number of changed characters is configured + + +The pwquality + difok + option sets the number of characters in a password that must not be present in the old password. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. + Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Create or modify a file ending in .conf + in the /etc/security/pwquality.conf.d/ + directory or the file /etc/security/pwquality.conf + and add or modify the following line to set difok + to 2 + or more. Ensure setting conforms to local site policy: + difok = 2 + + + Example: + + +# sed -ri 's/^\s*difok\s*=/# &/' /etc/security/pwquality.conf
+# printf '\n%s' "difok = 2" >> /etc/security/pwquality.conf.d/50-pwdifok.conf +
+ +Run the following script to remove setting difok + on the pam_pwquality.so + module in the PAM files: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ l_authselect_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+difok\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ done
+ authselect apply-changes
+} +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure password length is configured + + + minlen + - Minimum acceptable size for the new password (plus one if credits are not disabled which is the default). Cannot be set to lower value than 6. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Strong passwords protect systems from being hacked through brute force methods. + + + + pam_pwquality(8) + NIST SP 800-53 Rev. 5: IA-5 + + + + +Create or modify a file ending in .conf + in the /etc/security/pwquality.conf.d/ + directory or the file /etc/security/pwquality.conf + and add or modify the following line to set password length of 14 + or more characters. Ensure that password length conforms to local site policy: + + Example: + + +# sed -ri 's/^\s*minlen\s*=/# &/' /etc/security/pwquality.conf
+# printf '\n%s' "minlen = 14" >> /etc/security/pwquality.conf.d/50-pwlength.conf +
+ +Run the following script to remove setting minlen + on the pam_pwquality.so + module in the PAM files: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ l_authselect_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+minlen\s*=\s*[0-9]+)(.*$)/\1\4/' "$l_authselect_file"
+ done
+ authselect apply-changes
+} +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure password complexity is configured + + Password complexity can be set through: + + + minclass + - The minimum number of classes of characters required in a new password. (digits, uppercase, lowercase, others). e.g. minclass = 4 + requires digits, uppercase, lower case, and special characters. + + dcredit + - The maximum credit for having digits in the new password. If less than 0 + it is the minimum number of digits in the new password. e.g. dcredit = -1 + requires at least one digit + + ucredit + - The maximum credit for having uppercase characters in the new password. If less than 0 it is the minimum number of uppercase characters in the new password. e.g. ucredit = -1 + requires at least one uppercase character + + ocredit + - The maximum credit for having other characters in the new password. If less than 0 it is the minimum number of other characters in the new password. e.g. ocredit = -1 + requires at least one special character + + lcredit + - The maximum credit for having lowercase characters in the new password. If less than 0 it is the minimum number of lowercase characters in the new password. e.g. lcredit = -1 + requires at least one lowercase character + + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Strong passwords protect systems from being hacked through brute force methods. + + + + pam_pwquality(8) + PWQUALITY.CONF(5) + NIST SP 800-53 Rev. 5: IA-5 + + + + +Create or modify a file ending in .conf + in the /etc/security/pwquality.conf.d/ + directory or the file /etc/security/pwquality.conf + and add or modify the following line to set: + + + minclass = 4 + + + + --AND/OR-- + + + + dcredit = -_N_ + + + ucredit = -_N_ + + + ocredit = -_N_ + + + lcredit = -_N_ + + + + Example: + + +# sed -ri 's/^\s*minclass\s*=/# &/' /etc/security/pwquality.conf
+# printf '\n%s' "minclass = 4" >> /etc/security/pwquality.conf.d/50-pwcomplexity.conf +
+ + --AND/OR-- + + +# sed -ri 's/^\s*[dulo]credit\s*=/# &/' /etc/security/pwquality.conf
+# printf '%s\n' "dcredit = -1" "ucredit = -1" "ocredit = -1" "lcredit = -1" > /etc/security/pwquality.conf.d/50-pwcomplexity.conf +
+ +Run the following script to remove setting minclass +, dcredit +, ucredit +, lcredit +, and ocredit + on the pam_pwquality.so + module in the PAM files + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ l_authselect_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+minclass\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+dcredit\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+ucredit\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+lcredit\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+ocredit\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ done
+ authselect apply-changes
+} +
+
+
+
+ + + + + + +
+ + Ensure password same consecutive characters is configured + + +The pwquality + maxrepeat + option sets the maximum number of allowed same consecutive characters in a new password. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. + Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Create or modify a file ending in .conf + in the /etc/security/pwquality.conf.d/ + directory or the file /etc/security/pwquality.conf + and add or modify the following line to set maxrepeat + to 3 + or less and not 0 +. Ensure setting conforms to local site policy: + + Example: + + +# sed -ri 's/^\s*maxrepeat\s*=/# &/' /etc/security/pwquality.conf
+# printf '\n%s' "maxrepeat = 3" >> /etc/security/pwquality.conf.d/50-pwrepeat.conf +
+ +Run the following script to remove setting maxrepeat + on the pam_pwquality.so + module in the PAM files: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ l_authselect_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+maxrepeat\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ done
+ authselect apply-changes
+} +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure password maximum sequential characters is configured + + +The pwquality + maxsequence + option sets the maximum length of monotonic character sequences in the new password. Examples of such sequence are 12345 + or fedcb +. The check is disabled if the value is 0 +. + + Note: + Most such passwords will not pass the simplicity check unless the sequence is only a minor part of the password. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. + Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Create or modify a file ending in .conf + in the /etc/security/pwquality.conf.d/ + directory or the file /etc/security/pwquality.conf + and add or modify the following line to set maxsequence + to 3 + or less and not 0 +. Ensure setting conforms to local site policy: + + Example: + + +# sed -ri 's/^\s*maxsequence\s*=/# &/' /etc/security/pwquality.conf
+# printf '\n%s' "maxsequence = 3" >> /etc/security/pwquality.conf.d/50-pwmaxsequence.conf +
+ +Run the following script to remove setting maxsequence + on the pam_pwquality.so + module in the PAM files: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ l_authselect_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+maxsequence\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ done
+ authselect apply-changes
+} +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure password dictionary check is enabled + + +The pwquality + dictcheck + option sets whether to check for the words from the cracklib + dictionary. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + If the operating system allows the user to select passwords based on dictionary words, this increases the chances of password compromise by increasing the opportunity for successful guesses, and brute-force attacks. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Edit any file ending in .conf + in the /etc/security/pwquality.conf.d/ + directory and/or the file /etc/security/pwquality.conf + and comment out or remove any instance of dictcheck = 0 +: + + Example: + + # sed -ri 's/^\s*dictcheck\s*=/# &/' /etc/security/pwquality.conf /etc/security/pwquality.conf.d/*.conf + + +Run the following script to remove setting dictcheck + on the pam_pwquality.so + module in the PAM files: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ l_authselect_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+dictcheck\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ done
+ authselect apply-changes
+} +
+
+
+
+ + + + + + + + + + + + + + + + + + +
+ + Ensure password quality is enforced for the root user + + +If the pwquality + enforce_for_root + option is enabled, the module will return error on failed check even if the user changing the password is root. + This option is off by default which means that just the message about the failed check is printed but root can change the password anyway. + + Note: + The root is not asked for an old password so the checks that compare the old and new password are not performed. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. + Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Edit or add the following line in a *.conf + file in /etc/security/pwquality.conf.d + or in /etc/security/pwquality.conf +: + + Example: + + printf '\n%s\n' "enforce_for_root" >> /etc/security/pwquality.conf.d/50-pwroot.conf + + + + + + + + + + + + + +
+ + Configure pam_pwhistory module + + + pam_pwhistory + - PAM module to remember last passwords + + pam_history.so + module - This module saves the last passwords for each user in order to force password change history and keep the user from alternating between the same password too frequently. + +This module does not work together with kerberos. In general, it does not make much sense to use this module in conjunction with NIS + or LDAP +, since the old passwords are stored on the local machine and are not available on another machine for password history checking. + Options: + + + debug + - Turns on debugging via syslog(3). + + use_authtok + - When password changing enforce the module to use the new password provided by a previously stacked password module (this is used in the example of the stacking of the pam_passwdqc module + documented below). + + enforce_for_root + - If this option is set, the check is enforced for root, too. + + remember=<N> + - The last <N> + passwords for each user are saved. The default is 10 +. Value of 0 + makes the module to keep the existing contents of the opasswd file unchanged. + + retry=<N> + - Prompt user at most <N> + times before returning with error. The default is 1 +. + + authtok_type=<STRING> + - See pam_get_authtok(3) for more details. + + conf=</path/to/config-file> + - Use another configuration file instead of the default /etc/security/pwhistory.conf +. + + + Examples: + + +An example password section would be:
+
+ #%PAM-1.0
+ password required pam_pwhistory.so
+ password required pam_unix.so use_authtok
+
+In combination with pam_passwdqc:
+
+ #%PAM-1.0
+ password required pam_passwdqc.so config=/etc/passwdqc.conf
+ password required pam_pwhistory.so use_authtok
+ password required pam_unix.so use_authtok +
+ The options for configuring the module behavior are described in the pwhistory.conf(5) manual page. The options specified on the module command line override the values from the configuration file. + + pwhistory.conf + provides a way to configure the default settings for saving the last passwords for each user. This file is read by the pam_pwhistory module and is the preferred method over configuring pam_pwhistory directly. + +The file has a very simple name = value format with possible comments starting with # character. The whitespace at the beginning of line, end of line, and around the = + sign is ignored. + Options: + + + debug + - Turns on debugging via syslog(3). + + enforce_for_root + - If this option is set, the check is enforced for root, too. + + remember=<N> + - The last <N> + passwords for each user are saved. The default is 10 +. Value of 0 + makes the module to keep the existing contents of the opasswd file unchanged. + + retry=<N> + - Prompt user at most <N> + times before returning with error. The default is 1 +. + + file=</path/filename> + - Store password history in file + </path/filename> + + rather than the default location. The default location is /etc/security/opasswd +. + +
+ + Ensure password history remember is configured + + +The /etc/security/opasswd + file stores the users' old passwords and can be checked to ensure that users are not recycling recent passwords. The number of passwords remembered is set via the remember argument value in set for the pam_pwhistory + module. + + +remember=<N> - <N> + is the number of old passwords to remember + + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Requiring users not to reuse their passwords make it less likely that an attacker will be able to guess the password or use a compromised password. + + Note: + These change only apply to accounts configured on the local system. + + + + NIST SP 800-53 Rev. 5: IA-5(1) + + + + +Edit or add the following line in /etc/security/pwhistory.conf +: + remember = 24 + + +Run the following script to remove the remember + argument from the pam_pwhistory.so + module in /etc/pam.d/system-auth + and /etc/pam.d/password-auth +: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ l_authselect_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwhistory\.so.*)(\s+remember\s*=\s*\S+)(.*$)/\1\4/' "$l_authselect_file"
+ done
+ authselect apply-changes
+} +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure password history is enforced for the root user + + +If the pwhistory + enforce_for_root + option is enabled, the module will enforce password history for the root user as well + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Requiring users not to reuse their passwords make it less likely that an attacker will be able to guess the password or use a compromised password + + Note: + These change only apply to accounts configured on the local system. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Edit or add the following line in /etc/security/pwhistory.conf +: + enforce_for_root + + + + + + + + + + + + Ensure pam_pwhistory includes use_authtok + + + use_authtok + - When password changing enforce the module to set the new password to the one provided by a previously stacked password module + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + + use_authtok + allows multiple pam modules to confirm a new password before it is accepted. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Run the following script to verify the active authselect profile includes use_authtok + on the password stack's pam_pwhistory.so + module lines: + +#!/usr/bin/env bash
+
+{
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ grep -P -- '^\h*password\h+(requisite|required|sufficient)\h+pam_pwhistory\.so\h+([^#\n\r]+\h+)?use_authtok\b' "$l_pam_profile_path"/{password,system}-auth
+} +
+ + Example output: + + +/etc/authselect/custom/custom-profile/password-auth:password required pam_pwhistory.so use_authtok
+
+/etc/authselect/custom/custom-profile/system-auth:password required pam_pwhistory.so use_authtok +
+ + - IF - + the output does not include use_authtok +, run the following script: + +#!/usr/bin/env bash
+
+{
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ for l_authselect_file in "$l_pam_profile_path"/password-auth "$l_pam_profile_path"/system-auth; do
+ if grep -Pq '^\h*password\h+([^#\n\r]+)\h+pam_pwhistory\.so\h+([^#\n\r]+\h+)?use_authtok\b' "$l_authselect_file"; then
+ echo "- \"use_authtok\" is already set"
+ else
+ echo "- \"use_authtok\" is not set. Updating template"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwhistory\.so\s+.*)$/& use_authtok/g' "$l_authselect_file"
+ fi
+ done
+} +
+ +Run the following command to update the password-auth + and system-auth + files in /etc/pam.d + to include the use_authtok + argument on the password stack's pam_pwhistory.so + lines: + # authselect apply-changes + +
+
+
+ + + + + + + + +
+
+ + Configure pam_unix module + + +The pam_unix.so + module is the standard Unix authentication module. It uses standard calls from the system's libraries to retrieve and set account information as well as authentication. Usually this is obtained from the /etc/passwd + and the /etc/shadow + file as well if shadow is enabled. + + + Ensure pam_unix does not include nullok + + +The nullok + argument overrides the default action of pam_unix.so + to not permit the user access to a service if their official password is blank. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Using a strong password is essential to helping protect personal and sensitive information from unauthorized access + + + + + + + +Run the following script to verify that the active authselect profile's system-auth + and password-auth + files include {if not "without-nullok":nullok} + - OR - + don't include the nullok + option on the pam_unix.so + module: + +{
+ l_module_name="unix"
+ l_profile_name="$(head -1 /etc/authselect/authselect.conf)"
+ if [[ ! "$l_profile_name" =~ ^custom\/ ]]; then
+ echo " - Follow Recommendation \"Ensure custom authselect profile is used\" and then return to this Recommendation"
+ else
+ grep -P -- "\bpam_$l_module_name\.so\b" /etc/authselect/$l_profile_name/{password,system}-auth
+ fi
+} +
+ + Example output with a custom profile named "custom-profile": + + +/etc/authselect/custom/custom-profile/password-auth:auth sufficient pam_unix.so {if not "without-nullok":nullok}
+/etc/authselect/custom/custom-profile/password-auth:account required pam_unix.so
+/etc/authselect/custom/custom-profile/password-auth:password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok
+/etc/authselect/custom/custom-profile/password-auth:session required pam_unix.so
+
+/etc/authselect/custom/custom-profile/system-auth:auth sufficient pam_unix.so {if not "without-nullok":nullok}
+/etc/authselect/custom/custom-profile/system-auth:account required pam_unix.so
+/etc/authselect/custom/custom-profile/system-auth:password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok
+/etc/authselect/custom/custom-profile/system-auth:session required pam_unix.so +
+ + - IF - + any line is returned with nullok + that doesn't also include {if not "without-nullok":nullok} +, run the following script: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ l_file="/etc/authselect/$(head -1 /etc/authselect/authselect.conf | grep 'custom/')/$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so\s+.*)(nullok)(\s*.*)$/\1\2\4/g' $l_file
+ done
+} +
+ + - IF - + any line is returned with {if not "without-nullok":nullok} +, run the following command to enable the authselect without-nullok + feature: + # authselect enable-feature without-nullok + + +Run the following command to update the files in /etc/pam.d + to include pam_unix.so + without the nullok + argument: + # authselect apply-changes + +
+
+
+ + + + + + + + +
+ + Ensure pam_unix does not include remember + + +The remember=n + argument saves the last n passwords for each user in /etc/security/opasswd + in order to force password change history and keep the user from alternating between the same password too frequently. The MD5 password hash algorithm is used for storing the old passwords. Instead of this option the pam_pwhistory + module should be used. The pam_pwhistory + module saves the last n passwords for each user in /etc/security/opasswd + using the password hash algorithm set on the pam_unix + module. This allows for the sha512 + hash algorithm to be used. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + +The remember=n + argument should be removed to ensure a strong password hashing algorithm is being used. A stronger hash provides additional protection to the system by increasing the level of effort needed for an attacker to successfully determine local user's old passwords stored in /etc/security/opasswd +. + + + + + + + +Run the following script to verify the active authselect profile doesn't include the remember + argument on the pam_unix.so + module lines: + +#!/usr/bin/env bash
+
+{
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ grep -P -- '^\h*password\h+([^#\n\r]+\h+)pam_unix\.so\b' "$l_pam_profile_path"/{password,system}-auth
+} +
+ Output should be similar to: + +/etc/authselect/custom/custom-profile/password-auth:password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok
+
+/etc/authselect/custom/custom-profile/system-auth:password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok +
+ + - IF - + any line includes remember= +, run the following script to remove the remember= + from the pam_unix.so + lines in the active authselect profile password-auth + and system-auth` templates: + +#!/usr/bin/env bash
+
+{
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ for l_authselect_file in "$l_pam_profile_path"/password-auth "$l_pam_profile_path"/system-auth; do
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so\s+.*)(remember=[1-9][0-9]*)(\s*.*)$/\1\4/g' "$l_authselect_file"
+ done
+} +
+ +Run the following command to update the password-auth + and system-auth files in +/etc/pam.d to include pam_unix.so without the remember + argument: + # authselect apply-changes + +
+
+
+ + + + + + + + +
+ + Ensure pam_unix includes a strong password hashing algorithm + + A cryptographic hash function converts an arbitrary-length input into a fixed length output. Password hashing performs a one-way transformation of a password, turning the password into another string, called the hashed password. + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + Additional module options may be set, recommendation only covers those listed here. + The following command may be used to expire all non-system user ID's immediately and force them to change their passwords on next login. Any system accounts that need to be expired should be carefully done separately by the system administrator to prevent any potential problems. + # awk -F: '( $3&amp;amp;lt;'"$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)"' &amp;amp;&amp;amp; $1 != "nfsnobody" ) { print $1 }' /etc/passwd | xargs -n 1 chage -d 0 + + + + + +The SHA-512 + and yescrypt + algorithms provide a stronger hash than other algorithms used by Linux for password hash generation. A stronger hash provides additional protection to the system by increasing the level of effort needed for an attacker to successfully determine local user passwords. + + Note: + These changes only apply to the local system. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + + Note: + + + +It is highly recommended that the chosen hashing algorithm is consistent across /etc/libuser.conf +, /etc/login.defs +, /etc/pam.d/password-auth +, and /etc/pam.d/system-auth +. + +This only effects local users and passwords created after updating the files to use sha512 + or yescrypt +. If it is determined that the password algorithm being used is not sha512 + or yescrypt +, once it is changed, it is recommended that all user ID's be immediately expired and forced to change their passwords on next login. + + Run the following script to verify the active authselect profile includes a strong password hashing algorithm on the password stack's pam_unix.so module lines: + +#!/usr/bin/env bash
+
+{
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ grep -P -- '^\h*password\h+(requisite|required|sufficient)\h+pam_unix\.so\h+([^#\n\r]+\h+)?(sha512|yescrypt)\b' "$l_pam_profile_path"/{password,system}-auth
+} +
+ + Example output: + + +/etc/authselect/custom/custom-profile/password-auth:password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok
+
+/etc/authselect/custom/custom-profile/system-auth:password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok +
+ + - IF - + the output does not include either sha512 + - OR - + yescrypt +, or includes a different hashing algorithm, run the following script: + +#!/usr/bin/env bash
+
+{
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ for l_authselect_file in "$l_pam_profile_path"/password-auth "$l_pam_profile_path"/system-auth; do
+ if grep -Pq '^\h*password\h+()\h+pam_unix\.so\h+([^#\n\r]+\h+)?(sha512|yescrypt)\b' "$l_authselect_file"; then
+ echo "- A strong password hashing algorithm is correctly set"
+ elif grep -Pq '^\h*password\h+()\h+pam_unix\.so\h+([^#\n\r]+\h+)?(md5|bigcrypt|sha256|blowfish)\b' "$l_authselect_file"; then
+ echo "- A weak password hashing algorithm is set, updating to \"sha512\""
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so\s+.*)(md5|bigcrypt|sha256|blowfish)(\s*.*)$/\1\4 sha512/g' "$l_authselect_file"
+ else
+ echo "No password hashing algorithm is set, updating to \"sha512\""
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so\s+.*)$/& sha512/g' "$l_authselect_file"
+ fi
+ done
+} +
+ +Run the following command to update the password-auth + and system-auth + files in /etc/pam.d + to include pam_unix.so + with a strong password hashing algorithm argument: + # authselect apply-changes + +
+
+
+ + + + + + + + +
+ + Ensure pam_unix includes use_authtok + + + use_authtok + - When password changing enforce the module to set the new password to the one provided by a previously stacked password module + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + + use_authtok + allows multiple pam modules to confirm a new password before it is accepted. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Run the following script to verify the active authselect profile includes use_authtok + on the password stack's pam_unix.so + module lines: + +#!/usr/bin/env bash
+
+{
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ grep -P -- '^\h*password\h+(requisite|required|sufficient)\h+pam_unix\.so\h+([^#\n\r]+\h+)?use_authtok\b' "$l_pam_profile_path"/{password,system}-auth
+} +
+ + Example output: + + +/etc/authselect/custom/custom-profile/password-auth:password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok
+
+/etc/authselect/custom/custom-profile/system-auth:password sufficient pam_unix.so sha512 shadow {if not "without-nullok":nullok} use_authtok +
+ + - IF - + the output does not include use_authtok +, run the following script: + +#!/usr/bin/env bash
+
+{
+ l_pam_profile="$(head -1 /etc/authselect/authselect.conf)"
+ if grep -Pq -- '^custom\/' <<< "$l_pam_profile"; then
+ l_pam_profile_path="/etc/authselect/$l_pam_profile"
+ else
+ l_pam_profile_path="/usr/share/authselect/default/$l_pam_profile"
+ fi
+ for l_authselect_file in "$l_pam_profile_path"/password-auth "$l_pam_profile_path"/system-auth; do
+ if grep -Pq '^\h*password\h+([^#\n\r]+)\h+pam_unix\.so\h+([^#\n\r]+\h+)?use_authtok\b' "$l_authselect_file"; then
+ echo "- \"use_authtok\" is already set"
+ else
+ echo "- \"use_authtok\" is not set. Updating template"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_unix\.so\s+.*)$/& use_authtok/g' "$l_authselect_file"
+ fi
+ done
+} +
+ +Run the following command to update the password-auth + and system-auth + files in /etc/pam.d + to include the use_authtok + argument on the password stack's pam_unix.so + lines: + # authselect apply-changes + +
+
+
+ + + + + + + + +
+
+
+
+ + User Accounts and Environment + + This section provides guidance on setting up secure defaults for system and user accounts and their environment. + + + Configure shadow password suite parameters + + +While a majority of the password control parameters have been moved to PAM, some parameters are still available through the shadow password suite. Any changes made to /etc/login.defs +will only be applied if the usermod +command is used. If user IDs are added a different way, use the chage +command to effect changes to individual user IDs. + + + Ensure password expiration is configured + + +The PASS_MAX_DAYS + parameter in /etc/login.defs + allows an administrator to force passwords to expire once they reach a defined age. + + PASS_MAX_DAYS + <N> + - The maximum number of days a password may be used. If the password is older than this, a password change will be forced. If not specified, -1 will be assumed (which disables the restriction). + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + A value of -1 will disable password expiration. + + + + The window of opportunity for an attacker to leverage compromised credentials or successfully compromise credentials via an online brute force attack is limited by the age of the password. Therefore, reducing the maximum age of a password also reduces an attacker's window of opportunity. + We recommend a yearly password change. This is primarily because for all their good intentions users will share credentials across accounts. Therefore, even if a breach is publicly identified, the user may not see this notification, or forget they have an account on that site. This could leave a shared credential vulnerable indefinitely. Having an organizational policy of a 1-year (annual) password expiration is a reasonable compromise to mitigate this with minimal user burden. + + + + CIS Password Policy Guide + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the PASS_MAX_DAYS + parameter to conform to site policy in /etc/login.defs + : + PASS_MAX_DAYS 365 + + Modify user parameters for all users with a password set to match: + # chage --maxdays 365 <user> + + +Edit /etc/login.defs + and set PASS_MAX_DAYS + to a value greater than 0 + that follows local site policy: + + Example: + + PASS_MAX_DAYS 365 + + +Run the following command to modify user parameters for all users with a password set to a maximum age no greater than 365 + or less than 1 + that follows local site policy: + # chage --maxdays <N> <user> + + + Example: + + # awk -F: '($2~/^\$.+\$/) {if($5 > 365 || $5 < 1)system ("chage --maxdays 365 " $1)}' /etc/shadow + + + Warning: + If a password has been set at system install or kickstart, the last change date + field is not set, In this case, setting PASS_MAX_DAYS + will immediately expire the password. One possible solution is to populate the last change date + field through a command like: chage -d "$(date +%Y-%m-%d)" root + + Impact: + + The password expiration must be greater than the minimum days between password changes or users will be unable to change their password. + Excessive password expiration requirements do more harm than good, because these requirements make users select predictable passwords, composed of sequential words and numbers that are closely related to each other. In these cases, the next password can be predicted based on the previous one (incrementing a number used in the password for +example). Also, password expiration requirements offer no containment benefits because attackers will often use credentials as soon as they compromise them. Instead, immediate password changes should be based on key events including, but not limited to: + + Indication of compromise + Change of user roles + When a user leaves the organization. + + Not only does changing passwords every few weeks or months frustrate the user, it’s been suggested that it does more harm than good, because it could lead to bad practices by the user such as adding a character to the end of their existing password. + + + + + + + + + + + + + + + + + + + + + + + + Ensure minimum password days is configured + + + PASS_MIN_DAYS + < N +> - The minimum number of days allowed between password changes. Any password changes attempted sooner than this will be rejected. If not specified, 0 will be assumed (which disables the restriction). + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Users may have favorite passwords that they like to use because they are easy to remember and they believe that their password choice is secure from compromise. Unfortunately, passwords are compromised and if an attacker is targeting a specific individual user account, with foreknowledge of data about that user, reuse of old, potentially compromised passwords, may cause a security breach. + By restricting the frequency of password changes, an administrator can prevent users from repeatedly changing their password in an attempt to circumvent password reuse controls + + + + CIS Password Policy Guide + + + + +Edit /etc/login.defs + and set PASS_MIN_DAYS + to a value greater than 0 + that follows local site policy: + + Example: + + PASS_MIN_DAYS 1 + + Run the following command to modify user parameters for all users with a password set to a minimum days greater than zero that follows local site policy: + # chage --mindays <N> <user> + + + Example: + + # awk -F: '($2~/^\$.+\$/) {if($4 < 1)system ("chage --mindays 1 " $1)}' /etc/shadow + + Impact: + + If a users password is set by other personnel as a procedure in dealing with a lost or expired password, the user should be forced to update this "set" password with their own password. e.g. force "change at next logon". + +If it is not possible to have a user set their own password immediately, and this recommendation or local site procedure may cause a user to continue using a third party generated password, PASS_MIN_DAYS + for the effected user should be temporally changed to 0 +, to allow a user to change their password immediately. + For applications where the user is not using the password at console, the ability to "change at next logon" may be limited. This may cause a user to continue to use a password created by other personnel. + + + + + + + + + + + + + + + + + Ensure password expiration warning days is configured + + +The PASS_WARN_AGE + parameter in /etc/login.defs + allows an administrator to notify users that their password will expire in a defined number of days. + + PASS_WARN_AGE + <N> + - The number of days warning given before a password expires. A zero means warning is given only upon the day of expiration, a negative value means no warning is given. If not specified, no warning will be provided. + + + + + + + Applications + Protect + + + + + + Users + Protect + + + + + + Providing an advance warning that a password will be expiring gives users time to think of a secure password. Users caught unaware may choose a simple password or write it down where it may be discovered. + + + + + + + +Edit /etc/login.defs + and set PASS_WARN_AGE + to a value of 7 + or more that follows local site policy: + + Example: + + PASS_WARN_AGE 7 + + +Run the following command to modify user parameters for all users with a password set to a minimum warning to 7 + or more days that follows local site policy: + # chage --warndays <N> <user> + + + Example: + + # awk -F: '($2~/^\$.+\$/) {if($6 < 7)system ("chage --warndays 7 " $1)}' /etc/shadow + + + + + + + + + + + + + + + + + Ensure strong password hashing algorithm is configured + + A cryptographic hash function converts an arbitrary-length input into a fixed length output. Password hashing performs a one-way transformation of a password, turning the password into another string, called the hashed password. + + ENCRYPT_METHOD + (string) - This defines the system default encryption algorithm for encrypting passwords (if no algorithm are specified on the command line). It can take one of these values: + + + MD5 + - MD5-based algorithm will be used for encrypting password + + SHA256 + - SHA256-based algorithm will be used for encrypting password + + SHA512 + - SHA512-based algorithm will be used for encrypting password + + BCRYPT + - BCRYPT-based algorithm will be used for encrypting password + + YESCRYPT + - YESCRYPT-based algorithm will be used for encrypting password + + DES + - DES-based algorithm will be used for encrypting password (default) + + + Note: + + + +This parameter overrides the deprecated MD5_CRYPT_ENAB + variable. + This parameter will only affect the generation of group passwords. + The generation of user passwords is done by PAM and subject to the PAM configuration. + It is recommended to set this variable consistently with the PAM configuration. + + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + +The SHA-512 + and yescrypt + algorithms provide a stronger hash than other algorithms used by Linux for password hash generation. A stronger hash provides additional protection to the system by increasing the level of effort needed for an attacker to successfully determine local group passwords. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Edit /etc/login.defs + and set the ENCRYPT_METHOD + to SHA512 + or YESCRYPT +: + ENCRYPT_METHOD <HASHING_ALGORITHM> + + + Example: + + ENCRYPT_METHOD YESCRYPT + + + Note: + + + +This only effects local groups' passwords created after updating the file to use sha512 + or yescrypt +. + +If it is determined that the password algorithm being used is not sha512 + or yescrypt +, once it is changed, it is recommended that all group passwords be updated to use the stronger hashing algorithm. + +It is recommended that the chosen hashing algorithm is consistent across /etc/login.defs + and the PAM configuration + + + + + + + + + + + + Ensure inactive password lock is configured + + User accounts that have been inactive for over a given period of time can be automatically disabled. + + INACTIVE + - Defines the number of days after the password exceeded its maximum age where the user is expected to replace this password. + +The value is stored in the shadow password file. An input of 0 + will disable an expired password with no delay. An input of -1 + will blank the respective field in the shadow password file. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + A value of -1 would disable this setting. + + + + Inactive accounts pose a threat to system security since the users are not logging in to notice failed login attempts or other anomalies. + + + + CIS Password Policy Guide + + + + Run the following command to set the default password inactivity period to 45 days or less that meets local site policy: + # useradd -D -f <N> + + + Example: + + # useradd -D -f 45 + + +Run the following command to modify user parameters for all users with a password set to a inactive age of 45 + days or less that follows local site policy: + # chage --inactive <N> <user> + + + Example: + + # awk -F: '($2~/^\$.+\$/) {if($7 > 45 || $7 < 0)system ("chage --inactive 45 " $1)}' /etc/shadow + + + + + + + + + + + + + + + + + + + + + + + + Ensure all users last password change date is in the past + + All users should have a password change date in the past. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + If a user's recorded password change date is in the future, then they could bypass any set password expiration. + + + + + + + Investigate any users with a password change date in the future and correct them. Locking the account, expiring the password, or resetting the password manually may be appropriate. + + + + + + + + + + + + + Configure root and system accounts and environment + + + Ensure root is the only UID 0 account + + Any account with UID 0 has superuser privileges on the system. + + + +This access must be limited to only the default root + account and only from the system console. Administrative access must be through an unprivileged account using an approved mechanism as noted in the Recommendation "Ensure access to the su command is restricted". + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Run the following command to change the root + account UID to 0 +: + # usermod -u 0 root + + +Modify any users other than root + with UID 0 + and assign them a new UID. + + + + + + + + + + + + + Ensure root is the only GID 0 account + + +The usermod + command can be used to specify which group the root + account belongs to. This affects permissions of files that are created by the root + account. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Using GID 0 for the root + account helps prevent root + -owned files from accidentally becoming accessible to non-privileged users. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Run the following command to set the root + user's GID to 0 +: + # usermod -g 0 root + + +Run the following command to set the root + group's GID to 0 +: + # groupmod -g 0 root + + +Remove any users other than the root + user with GID 0 or assign them a new GID if appropriate. + + + + + + + + + + + + + Ensure group root is the only GID 0 group + + +The groupmod + command can be used to specify which group the root + group belongs to. This affects permissions of files that are group owned by the root + group. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Using GID 0 for the root + group helps prevent root + group owned files from accidentally becoming accessible to non-privileged users. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Run the following command to set the root + group's GID to 0 +: + # groupmod -g 0 root + + +Remove any groups other than the root + group with GID 0 or assign them a new GID if appropriate. + + + + + + + + + + + + + Ensure root account access is controlled + + There are a number of methods to access the root account directly. Without a password set any user would be able to gain access and thus control over the entire system. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Access to root + should be secured at all times. + + + + + + + +Run the following command to set a password for the root + user: + # passwd root + + + - OR - + + +Run the following command to lock the root + user account: + # usermod -L root + + Impact: + + If there are any automated processes that relies on access to the root account without authentication, they will fail after remediation. + + + + + + + + + + + + + + + + + Ensure root path integrity + + +The root + user can execute any command on the system and could be fooled into executing programs unintentionally if the PATH + is not set correctly. + + + +Including the current working directory (.) or other writable directory in root +'s executable path makes it likely that an attacker can gain superuser access by forcing an administrator operating as root + to execute a Trojan horse program. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Correct or justify any: + + Locations that are not directories + +Empty directories ( :: +) + +Trailing ( : +) + +Current working directory ( . +) + +Non root + owned directories + +Directories that less restrictive than mode 0755 + + + + + + + + + + + + + + Ensure root user umask is configured + + +The user file-creation mode mask ( umask +) is used to determine the file permission for newly created directories and files. In Linux, the default permissions for any newly created directory is 0777 ( rwxrwxrwx +), and for any newly created file it is 0666 ( rw-rw-rw- +). The umask + modifies the default Linux permissions by restricting (masking) these permissions. The umask + is not simply subtracted, but is processed bitwise. Bits set in the umask + are cleared in the resulting file mode. + + umask + can be set with either Octal + or Symbolic + values: + + + Octal + (Numeric) Value - Represented by either three or four digits. ie umask 0027 + or umask 027 +. If a four digit umask is used, the first digit is ignored. The remaining three digits effect the resulting permissions for user, group, and world/other respectively. + + Symbolic + Value - Represented by a comma separated list for User u +, group g +, and world/other o +. The permissions listed are not masked by umask +. ie a umask + set by umask u=rwx,g=rx,o= + is the Symbolic + equivalent of the Octal + umask 027 +. This umask + would set a newly created directory with file mode drwxr-x--- + and a newly created file with file mode rw-r----- +. + + + root user Shell Configuration Files: + + + + /root/.bash_profile + - Is executed to configure the root users' shell before the initial command prompt. Is only read by login shells. + + + /root/.bashrc + - Is executed for interactive shells. only read by a shell that's both interactive and non-login + + + + umask + is set by order of precedence. If umask + is set in multiple locations, this order of precedence will determine the system's default umask +. + + Order of precedence: + + + + /root/.bash_profile + + + /root/.bashrc + + The system default umask + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Setting a secure value for umask + ensures that users make a conscious choice about their file permissions. A permissive umask + value could result in directories or files with excessive permissions that can be read and/or written to by unauthorized users. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Edit /root/.bash_profile + and /root/.bashrc + and remove, comment out, or update any line with umask + to be 0027 + or more restrictive. + + + + + + + + + + + + + + Ensure system accounts do not have a valid login shell + + There are a number of accounts provided with most distributions that are used to manage applications and are not intended to provide an interactive shell. Furthermore, a user may add special accounts that are not intended to provide an interactive shell. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +The root +, sync +, shutdown +, and halt + users are exempted from requiring a non-login shell. + + + + +It is important to make sure that accounts that are not being used by regular users are prevented from being used to provide an interactive shell. By default, most distributions set the password field for these accounts to an invalid string, but it is also recommended that the shell field in the password file be set to the nologin + shell. This prevents the account from potentially being used to run any commands. + + + + NIST SP 800-53 Rev. 5: AC-2(5), AC-3, AC-11, MP-2 + + + + +Run the following command to set the shell for any service accounts returned by the audit to nologin +: + # usermod -s $(command -v nologin) <user> + + + Example script: + + +#!/usr/bin/env bash
+
+{
+ l_valid_shells="^($( awk -F\/ '$NF != "nologin" {print}' /etc/shells | sed -rn '/^\//{s,/,\\\\/,g;p}' | paste -s -d '|' - ))$"
+ awk -v pat="$l_valid_shells" -F: '($1!~/^(root|halt|sync|shutdown|nfsnobody)$/ && ($3<'"$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)"' || $3 == 65534) && $(NF) ~ pat) {system ("usermod -s '"$(command -v nologin)"' " $1)}' /etc/passwd
+} +
+
+
+
+ + + + + + +
+ + Ensure accounts without a valid login shell are locked + + There are a number of accounts provided with most distributions that are used to manage applications and are not intended to provide an interactive shell. Furthermore, a user may add special accounts that are not intended to provide an interactive shell. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is important to make sure that accounts that are not being used by regular users are prevented from being used to provide an interactive shell. By default, most distributions set the password field for these accounts to an invalid string, but it is also recommended that the shell field in the password file be set to the nologin + shell. This prevents the account from potentially being used to run any commands. + + + + NIST SP 800-53 Rev. 5: AC-2(5), AC-3, AC-11, MP-2 + + + + Run the following command to lock any non-root accounts without a valid login shell returned by the audit: + # usermod -L <user> + + + Example script: +: + +#!/usr/bin/env bash
+
+{
+ l_valid_shells="^($(awk -F\/ '$NF != "nologin" {print}' /etc/shells | sed -rn '/^\//{s,/,\\\\/,g;p}' | paste -s -d '|' - ))$"
+ while IFS= read -r l_user; do
+ passwd -S "$l_user" | awk '$2 !~ /^L/ {system ("usermod -L " $1)}'
+ done < <(awk -v pat="$l_valid_shells" -F: '($1 != "root" && $(NF) !~ pat) {print $1}' /etc/passwd)
+} +
+
+
+
+ + + + + + +
+
+ + Configure user default environment + + + Ensure nologin is not listed in /etc/shells + + + /etc/shells + is a text file which contains the full pathnames of valid login shells. This file is consulted by chsh + and available to be queried by other programs. + Be aware that there are programs which consult this file to find out if a user is a normal user; for example, FTP daemons traditionally disallow access to users with shells not included in this file. + + + +A user can use chsh + to change their configured shell. + +If a user has a shell configured that isn't in in /etc/shells +, then the system assumes that they're somehow restricted. In the case of chsh + it means that the user cannot change that value. + Other programs might query that list and apply similar restrictions. + +By putting nologin + in /etc/shells +, any user that has nologin + as its shell is considered a full, unrestricted user. This is not the expected behavior for nologin +. + + shells(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit /etc/shells + and remove any lines that include nologin + + + + + + + + + + + + Ensure default user shell timeout is configured + + + TMOUT + is an environmental setting that determines the timeout of a shell in seconds. + + +TMOUT= n + - Sets the shell timeout to n + seconds. A setting of TMOUT=0 + disables timeout. + readonly TMOUT- Sets the TMOUT environmental variable as readonly, preventing unwanted modification during run-time. + export TMOUT - exports the TMOUT variable + + + System Wide Shell Configuration Files: + + + + /etc/profile + - used to set system wide environmental variables on users shells. The variables are sometimes the same ones that are in the .bash_profile +, however this file is used to set an initial PATH or PS1 for all shell users of the system. +is only executed for interactive login + shells, or shells executed with the --login parameter. + + + /etc/profile.d + - /etc/profile + will execute the scripts within /etc/profile.d/*.sh +. It is recommended to place your configuration in a shell script within /etc/profile.d + to set your own system wide environmental variables. + + /etc/bashrc + - System wide version of .bashrc +. In Fedora derived distributions, /etc/bashrc + also invokes /etc/profile.d/*.sh if non-login + shell, but redirects output to /dev/null + if non-interactive. + +Is only executed for interactive + shells or if BASH_ENV + is set to /etc/bashrc +. + + + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + The audit and remediation in this recommendation apply to bash and shell. If other shells are supported on the system, it is recommended that their configuration files also are checked. Other methods of setting a timeout exist for other shells not covered here. + Ensure that the timeout conforms to your local policy. + + + + Setting a timeout value reduces the window of opportunity for unauthorized user access to another user's shell session that has been left unattended. It also ends the inactive session and releases the resources associated with that session. + + + + + + + +Review /etc/bashrc +, /etc/profile +, and all files ending in *.sh + in the /etc/profile.d/ + directory and remove or edit all TMOUT=_n_ + entries to follow local site policy. TMOUT + should not exceed 900 or be equal to 0 +. + +Configure TMOUT + in one + of the following files: + + +A file in the /etc/profile.d/ + directory ending in .sh + + + /etc/profile + + + /etc/bashrc + + + + +Example command to set TMOUT to 900 + seconds in a file in /etc/profile.d/ +: + + # printf '%s\n' "# Set TMOUT to 900 seconds" "typeset -xr TMOUT=900" > /etc/profile.d/50-tmout.sh + + + + TMOUT + configuration examples: + + typeset -xr TMOUT=900 + + Deprecated methods: + + As multiple lines: + + +TMOUT=900
+readonly TMOUT
+export TMOUT +
+ + As a single line: + + readonly TMOUT=900 ; export TMOUT + +
+
+
+ + + + + + + +
+ + Ensure default user umask is configured + + +The user file-creation mode mask ( umask +) is used to determine the file permission for newly created directories and files. In Linux, the default permissions for any newly created directory is 0777 ( rwxrwxrwx +), and for any newly created file it is 0666 ( rw-rw-rw- +). The umask + modifies the default Linux permissions by restricting (masking) these permissions. The umask + is not simply subtracted, but is processed bitwise. Bits set in the umask + are cleared in the resulting file mode. + + umask + can be set with either Octal + or Symbolic + values: + + + Octal + (Numeric) Value - Represented by either three or four digits. ie umask 0027 + or umask 027 +. If a four digit umask is used, the first digit is ignored. The remaining three digits effect the resulting permissions for user, group, and world/other respectively. + + Symbolic + Value - Represented by a comma separated list for User u +, group g +, and world/other o +. The permissions listed are not masked by umask +. ie a umask + set by umask u=rwx,g=rx,o= + is the Symbolic + equivalent of the Octal + umask 027 +. This umask + would set a newly created directory with file mode drwxr-x--- + and a newly created file with file mode rw-r----- +. + + +The default umask + can be set to use the pam_umask + module or in a System Wide Shell Configuration File +. The user creating the directories or files has the discretion of changing the permissions via the chmod command, or choosing a different default umask + by adding the umask + command into a User Shell Configuration File +, ( .bash_profile + or .bashrc +), in their home directory. + + Setting the default umask: + + + +pam_umask module: + + +will set the umask according to the system default in /etc/login.defs + and user settings, solving the problem of different umask + settings with different shells, display managers, remote sessions etc. + + umask=<mask> + value in the /etc/login.defs + file is interpreted as Octal + +Setting USERGROUPS_ENAB + to yes in /etc/login.defs + (default): + + +will enable setting of the umask + group bits to be the same as owner bits. (examples: 022 -> 002, 077 -> 007) for non-root users, if the uid + is the same as gid +, and username + is the same as the <primary group name> + + userdel will remove the user's group if it contains no more members, and useradd will create by default a group with the name of the user + + + + + + System Wide Shell Configuration File +: + + + /etc/profile + - used to set system wide environmental variables on users shells. The variables are sometimes the same ones that are in the .bash_profile +, however this file is used to set an initial PATH or PS1 for all shell users of the system. +is only executed for interactive login + shells, or shells executed with the --login parameter. + + + /etc/profile.d + - /etc/profile + will execute the scripts within /etc/profile.d/*.sh +. It is recommended to place your configuration in a shell script within /etc/profile.d + to set your own system wide environmental variables. + + /etc/bashrc + - System wide version of .bashrc +. In Fedora derived distributions, etc/bashrc + also invokes /etc/profile.d/*.sh if non-login + shell, but redirects output to /dev/null + if non-interactive. + +Is only executed for interactive + shells or if BASH_ENV + is set to /etc/bashrc +. + + + + + + User Shell Configuration Files: + + + + ~/.bash_profile + - Is executed to configure your shell before the initial command prompt. Is only read by login shells. + + + ~/.bashrc + - Is executed for interactive shells. only read by a shell that's both interactive and non-login + + + + umask + is set by order of precedence. If umask + is set in multiple locations, this order of precedence will determine the system's default umask +. + + Order of precedence: + + + +A file in /etc/profile.d/ + ending in .sh + - This will override any other system-wide umask + setting + +In the file /etc/profile + + +On the pam_umask.so + module in /etc/pam.d/postlogin + + +In the file /etc/login.defs + + +In the file /etc/default/login + + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Other methods of setting a default user umask exist + If other methods are in use in your environment they should be audited + The default user umask can be overridden with a user specific umask + +The user creating the directories or files has the discretion of changing the permissions: + + Using the chmod command + Setting a different default umask by adding the umask command into a User Shell Configuration File, (.bashrc), in their home directory + Manually changing the umask for the duration of a login session by running the umask command + + + + + + + +Setting a secure default value for umask + ensures that users make a conscious choice about their file permissions. A permissive umask + value could result in directories or files with excessive permissions that can be read and/or written to by unauthorized users. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following script and perform the instructions in the output to set the default umask to 027 + or more restrictive: + +#!/usr/bin/env bash
+
+{
+ l_output="" l_output2="" l_out=""
+ file_umask_chk()
+ {
+ if grep -Psiq -- '^\h*umask\h+(0?[0-7][2-7]7|u(=[rwx]{0,3}),g=([rx]{0,2}),o=)(\h*#.*)?$' "$l_file"; then
+ l_out="$l_out\n - umask is set correctly in \"$l_file\""
+ elif grep -Psiq -- '^\h*umask\h+(([0-7][0-7][01][0-7]\b|[0-7][0-7][0-7][0-6]\b)|([0-7][01][0-7]\b|[0-7][0-7][0-6]\b)|(u=[rwx]{1,3},)?(((g=[rx]?[rx]?w[rx]?[rx]?\b)(,o=[rwx]{1,3})?)|((g=[wrx]{1,3},)?o=[wrx]{1,3}\b)))' "$l_file"; then
+ l_output2="$l_output2\n - \"$l_file\""
+ fi
+ }
+ while IFS= read -r -d $'\0' l_file; do
+ file_umask_chk
+ done < <(find /etc/profile.d/ -type f -name '*.sh' -print0)
+ [ -n "$l_out" ] && l_output="$l_out"
+ l_file="/etc/profile" && file_umask_chk
+ l_file="/etc/bashrc" && file_umask_chk
+ l_file="/etc/bash.bashrc" && file_umask_chk
+ l_file="/etc/pam.d/postlogin"
+ if grep -Psiq '^\h*session\h+[^#\n\r]+\h+pam_umask\.so\h+([^#\n\r]+\h+)?umask=(([0-7][0-7][01][0-7]\b|[0-7][0-7][0-7][0-6]\b)|([0-7][01][0-7]\b))' "$l_file"; then
+ l_output2="$l_output2\n - \"$l_file\""
+ fi
+ l_file="/etc/login.defs" && file_umask_chk
+ l_file="/etc/default/login" && file_umask_chk
+ if [ -z "$l_output2" ]; then
+ echo -e " - No files contain a UMASK that is not restrictive enough\n No UMASK updates required to existing files"
+ else
+ echo -e "\n - UMASK is not restrictive enough in the following file(s):$l_output2\n\n- Remediation Procedure:\n - Update these files and comment out the UMASK line\n or update umask to be \"0027\" or more restrictive"
+ fi
+ if [ -n "$l_output" ]; then
+ echo -e "$l_output"
+ else
+ echo -e " - Configure UMASK in a file in the \"/etc/profile.d/\" directory ending in \".sh\"\n\n Example Command (Hash to represent being run at a root prompt):\n\n# printf '%s\\\n' \"umask 027\" > /etc/profile.d/50-systemwide_umask.sh\n"
+ fi
+} +
+ + Notes: + + + This method only applies to bash and shell. If other shells are supported on the system, it is recommended that their configuration files also are checked + +If the pam_umask.so + module is going to be used to set umask +, ensure that it's not being overridden by another setting. Refer to the PAM_UMASK(8) man page for more information + +
+
+
+ + + + + + +
+
+
+
+ + Logging and Auditing + + The items in this section describe how to configure logging, log monitoring, and auditing, using tools included in most distributions. + +It is recommended that rsyslog +be used for logging (with logwatch +providing summarization) and auditd + be used for auditing (with aureport +providing summarization) to automatically monitor logs for intrusion attempts and other suspicious system behavior. + In addition to the local log files created by the steps in this section, it is also recommended that sites collect copies of their system logs on a secure, centralized log server via an encrypted connection. Not only does centralized logging help sites correlate events that may be occurring on multiple systems, but having a second copy of the system log information may be critical after a system compromise where the attacker has modified the local log files on the affected system(s). If a log correlation system is deployed, configure it to process the logs described in this section. + +Because it is often necessary to correlate log information from many different systems (particularly after a security incident) it is recommended that the time be synchronized among systems and devices connected to the local network. The standard Internet protocol for time synchronization is the Network Time Protocol (NTP), which is supported by most network-ready devices. Reference < http://chrony.tuxfamily.org/ +> manual page for more information on configuring chrony. + It is important that all logs described in this section be monitored on a regular basis and correlated to determine trends. A seemingly innocuous entry in one log could be more significant when compared to an entry in another log. + + Note on log file permissions: + There really isn't a "one size fits all" solution to the permissions on log files. Many sites utilize group permissions so that administrators who are in a defined security group, such as "wheel" do not have to elevate privileges to root in order to read log files. Also, if a third party log aggregation tool is used, it may need to have group permissions to read the log files, which is preferable to having it run setuid to root. Therefore, there are two remediation and audit steps for log file permissions. One is for systems that do not have a secured group method implemented that only permits root to read the log files ( root:root 600 +). The other is for sites that do have such a setup and are designated as root:securegrp 640 +where securegrp + is the defined security group (in some cases wheel +). + + + Configure Integrity Checking + + AIDE is a file integrity checking tool, similar in nature to Tripwire. While it cannot prevent intrusions, it can detect unauthorized changes to configuration files by alerting when the files are changed. When setting up AIDE, decide internally what the site policy will be concerning integrity checking. Review the AIDE quick start guide and AIDE documentation before proceeding. + + + Ensure AIDE is installed + + Advanced Intrusion Detection Environment (AIDE) is a intrusion detection tool that uses predefined rules to check the integrity of files and directories in the Linux operating system. AIDE has its own database to check the integrity of files and directories. + + aide + takes a snapshot of files and directories including modification times, permissions, and file hashes which can then be used to compare against the current state of the filesystem to detect modifications to the system. + + + + + + + Data + Detect + + + + + + Data + Detect + + + + + +The prelinking feature can interfere with aide + because it alters binaries to speed up their start up times. Run prelink -ua + to restore the binaries to their prelinked state, thus avoiding false positives from aide +. + + + + By monitoring the filesystem state compromised files can be detected to prevent or limit the exposure of accidental or malicious misconfigurations or modified binaries. + + + + AIDE stable manual: http://aide.sourceforge.net/stable/manual.html + NIST SP 800-53 Rev. 5: AU-2 + + + + +Run the following command to install aide +: + # dnf install aide + + +Configure aide + as appropriate for your environment. Consult the aide + documentation for options. + +Initialize aide +: + Run the following commands: + # aide --init + + # mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz + + + + + + + + + + + + Ensure filesystem integrity is regularly checked + + Periodic checking of the filesystem integrity is needed to detect changes to the filesystem. + + + + + + + Data + Detect + + + + + + Data + Detect + + + + + The checking in this recommendation occurs every day at 5am. Alter the frequency and time of the checks in compliance with site policy. + + + + Periodic file checking allows the system administrator to determine on a regular basis if critical files have been changed in an unauthorized fashion. + + + + https://github.com/konstruktoid/hardening/blob/master/config/aidecheck.service + https://github.com/konstruktoid/hardening/blob/master/config/aidecheck.timer + NIST SP 800-53 Rev. 5: AU-2 + + + + + - IF - + cron + will be used to schedule and run aide check + Run the following command: + # crontab -u root -e + + Add the following line to the crontab: + 0 5 * * * /usr/sbin/aide --check + + + - OR - + + + - IF - + aidecheck.service + and aidecheck.timer + will be used to schedule and run aide check: + +Create or edit the file /etc/systemd/system/aidecheck.service + and add the following lines: + +[Unit]
+Description=Aide Check
+
+[Service]
+Type=simple
+ExecStart=/usr/sbin/aide --check
+
+[Install]
+WantedBy=multi-user.target +
+ +Create or edit the file /etc/systemd/system/aidecheck.timer + and add the following lines: + +[Unit]
+Description=Aide check every day at 5AM
+
+[Timer]
+OnCalendar=*-*-* 05:00:00
+Unit=aidecheck.service
+
+[Install]
+WantedBy=multi-user.target +
+ Run the following commands: + +# chown root:root /etc/systemd/system/aidecheck.*
+# chmod 0644 /etc/systemd/system/aidecheck.*
+
+# systemctl daemon-reload
+
+# systemctl enable aidecheck.service
+# systemctl --now enable aidecheck.timer +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure cryptographic mechanisms are used to protect the integrity of audit tools + + Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + + + Protecting the integrity of the tools used for auditing purposes is a critical step toward ensuring the integrity of audit information. Audit information includes all information (e.g., audit records, audit settings, and audit reports) needed to successfully audit information system activity. + Attackers may replace the audit tools or inject code into the existing tools with the purpose of providing the capability to hide or erase system activity from the audit logs. + Audit tools should be cryptographically signed in order to provide the capability to identify when the audit tools have been modified, manipulated, or replaced. An example is a checksum hash of the file or files. + + AIDE.CONF(5) + + + + Run the following command to determine the absolute path to the non-symlinked version on the audit tools: + # readlink -f /sbin + + +The output will be either /usr/sbin + - OR - + /sbin +. Ensure the correct path is used. + +Edit /etc/aide.conf + and add or update the following selection lines replacing <PATH> + with the correct path returned in the command above: + +# Audit Tools
+<PATH>/auditctl p+i+n+u+g+s+b+acl+xattrs+sha512
+<PATH>/auditd p+i+n+u+g+s+b+acl+xattrs+sha512
+<PATH>/ausearch p+i+n+u+g+s+b+acl+xattrs+sha512
+<PATH>/aureport p+i+n+u+g+s+b+acl+xattrs+sha512
+<PATH>/autrace p+i+n+u+g+s+b+acl+xattrs+sha512
+<PATH>/augenrules p+i+n+u+g+s+b+acl+xattrs+sha512 +
+ + Example + + +# printf '\n%s' "# Audit Tools" "$(readlink -f /sbin/auditctl) p+i+n+u+g+s+b+acl+xattrs+sha512" \
+"$(readlink -f /sbin/auditd) p+i+n+u+g+s+b+acl+xattrs+sha512" \
+"$(readlink -f /sbin/ausearch) p+i+n+u+g+s+b+acl+xattrs+sha512" \
+"$(readlink -f /sbin/aureport) p+i+n+u+g+s+b+acl+xattrs+sha512" \
+"$(readlink -f /sbin/autrace) p+i+n+u+g+s+b+acl+xattrs+sha512" \
+"$(readlink -f /sbin/augenrules) p+i+n+u+g+s+b+acl+xattrs+sha512" >> /etc/aide.conf +
+ + Note: - IF - + /etc/aide.conf + includes a @@x_include + statement: + + Example: + + @@x_include /etc/aide.conf.d ^[a-zA-Z0-9_-]+$ + + + + @@x_include + FILE + + @@x_include + DIRECTORY REGEX + + + @x_include + is identical to @@include +, except that if a config file is executable it is run and the output is used as config. + If the executable file exits with status greater than zero or writes to stderr aide stops with an error. + For security reasons DIRECTORY and each executable config file must be owned by the current user and must not be group or world-writable. + + + +
+
+
+ + + + + + +
+
+ + System Logging + + Logging services should be configured to prevent information leaks and to aggregate logs on a remote server so that they can be reviewed in the event of a system compromise. A centralized log server provides a single point of entry for further analysis, monitoring and filtering. + + Security principals for logging + + + Ensure transport layer security is implemented between the client and the log server. + Ensure that logs are rotated as per the environment requirements. + Ensure all locally generated logs have the appropriate permissions. + Ensure all security logs are sent to a remote log server. + Ensure the required events are logged. + + + What is covered + + +This section will cover the minimum best practices for the usage of + either + + rsyslog + + or + + journald +. The recommendations are written such that each is wholly independent of each other and + only one is implemented + +. + + +If your organization makes use of an enterprise wide logging system completely outside of rsyslog + or journald +, then the following recommendations do not directly apply. However, the principals of the recommendations should be followed regardless of what solution is implemented. If the enterprise solution incorporates either of these tools, careful consideration should be given to the following recommendations to determine exactly what applies. + +Should your organization make use of both rsyslog + and journald +, take care how the recommendations may or may not apply to you. + + + What is not covered + + + +Enterprise logging systems not utilizing rsyslog + or journald +. +As logging is very situational and dependent on the local environment, not everything can be covered here. + +Transport layer security should be applied to all remote logging functionality. Both rsyslog + and journald + supports secure transport and should be configured as such. + The log server. There are a multitude of reasons for a centralized log server (and keeping a short period of logging on the local system), but the log server is out of scope for these recommendations. + + + + Configure systemd-journald service + + + systemd-journald + is a system service that collects and stores logging data. It creates and maintains structured, indexed journals based on logging information that is received from a variety of sources: + + Kernel log messages, via kmsg + Simple system log messages, via the libc syslog call + Structured system log messages via the native Journal API + Standard output and standard error of service units + Audit records, originating from the kernel audit subsystem + + The daemon will implicitly collect numerous metadata fields for each log messages in a secure and unfakeable way. See systemd.journal-fields man page for more information about the collected metadata. + +The journal service stores log data either persistently below /var/log/journal + or in a volatile way below /run/log/journal/ +. By default, log data is stored persistently if /var/log/journal/ + exists during boot, with an implicit fallback to volatile storage. Use Storage= + in journald.conf + to configure where log data is placed, independently of the existence of /var/log/journal/ +. + +On systems where /var/log/journal/ + does not exist but where persistent logging is desired, and the default journald.conf + is used, it is sufficient to create the directory and ensure it has the correct access modes and ownership. + + Note: + systemd-journald.service + must be configured appropriately for either journald + - OR - + rsyslog + to operate effectively. + + + Ensure journald service is enabled and active + + +Ensure that the systemd-journald + service is enabled to allow capturing of logging events. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + +If the systemd-journald + service is not enabled to start on boot, the system will not capture logging events. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-7 AU-12 + + + + +Run the following commands to unmask and start systemd-journald.service + + +# systemctl unmask systemd-journald.service
+# systemctl start systemd-journald.service +
+
+
+
+ + + + + + + + + + +
+ + Ensure journald log file access is configured + + Journald will create logfiles that do not already exist on the system. This setting controls what permissions will be applied to these newly created files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +See man 5 tmpfiles.d + for detailed information on the permission sets for the relevant log files. +Further information with examples can be found at https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html + + + + + It is important to ensure that log files have the correct permissions to ensure that sensitive data is archived and protected. + + + + NIST SP 800-53 Rev. 5: AC-3, AU-2, AU-12, MP-2, SI-5 + + + + +If the default configuration is not appropriate for the site specific requirements, copy /usr/lib/tmpfiles.d/systemd.conf + to /etc/tmpfiles.d/systemd.conf + and modify as required. Requirements is either 0640 + or site policy if that is less restrictive. + + + + + + Ensure journald log file rotation is configured + + +Journald includes the capability of rotating log files regularly to avoid filling up the system with logs or making the logs unmanageably large. The file /etc/systemd/journald.conf + is the configuration file used to specify how logs generated by Journald should be rotated. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + +See man 5 journald.conf + for detailed information regarding the parameters in use. + + + + By keeping the log files smaller and more manageable, a system administrator can easily archive these files to another system and spend less time looking through inordinately large log files. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-7, AU-12 + https://man7.org/linux/man-pages/man5/journald.conf.5.html + + + + +Edit /etc/systemd/journald.conf + or a file ending in .conf + the /etc/systemd/journald.conf.d/ + directory. Set the following parameters in the [Journal] + section to ensure logs are rotated according to site policy. The settings should be carefully understood as there are specific edge cases and prioritization of parameters. + + Example Configuration + + +[Journal]
+SystemMaxUse=1G
+SystemKeepFree=500M
+RuntimeMaxUse=200M
+RuntimeKeepFree=50M
+MaxFileSec=1month +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, the setting will be overwritten +
+
+
+
+ + Ensure only one logging system is in use + + +Best practices recommend that a single centralized logging system be used for log management, choose a single service either rsyslog + - OR - + journald + to be used as a single centralized logging system. + + + +Configuring only one logging service either rsyslog + - OR - + journald + avoids redundancy, optimizes resources, simplifies configuration and management, and ensures consistency. + + + + + + +Determine whether to use journald + - OR - + rsyslog + depending on site needs + +Configure systemd-jounald.service + + +Configure only ONE + either journald + - OR - + rsyslog + and complete the recommendations in that subsection + Return to this recommendation to ensure only one logging system is in use + + Impact: + + Transitioning from one logging service to another can be complex and time consuming, it involves reconfiguration and may result in data loss if not managed and reconfigured correctly. + + + + + + + + + + + +
+ + Configure journald + + Included in the systemd suite is a journaling service called systemd-journald.service for the collection and storage of logging data. It creates and maintains structured, indexed journals based on logging information that is received from a variety of sources such as: + Classic RFC3164 BSD syslog via the /dev/log socket +STDOUT/STDERR of programs via StandardOutput=journal + StandardError=journal in service files (both of which are default settings) +Kernel log messages via the /dev/kmsg device node +Audit records via the kernel’s audit subsystem +Structured log messages via journald’s native protocol +Any changes made to the systemd-journald configuration will require a re-start of systemd-journald + + Note: + + + - IF - + rsyslog + will be used for remote logging on the system this subsection can be skipped + + + Configure systemd-journal-remote + + +The systemd-journal-remote + package includes systemd-journal-upload +. + + systemd-journal-upload + will upload journal entries to the URL specified with --url=. This program reads journal entries from one or more journal files, similarly to journalctl. + + systemd-journal-upload + transfers the raw content of journal file and uses HTTP as a transport protocol. + + systemd-journal-upload.service + is a system service that uses systemd-journal-upload + to upload journal entries to a server. It uses the configuration in journal-upload.conf +. + + Note: + - IF - + rsyslog + is in use this subsection can be skipped. + + + Ensure systemd-journal-remote is installed + + +Journald systemd-journal-remote + supports the ability to send log events it gathers to a remote log host or to receive messages from remote hosts, thus enabling centralized log management. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + Storing log data on a remote host protects log integrity from local attacks. If an attacker gains root access on the local system, they could tamper with or remove log data that is stored on the local system. + + Note: + This recommendation +only applies if journald + is the chosen method for client side logging +. Do not apply this recommendation if rsyslog + is used. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-7 AU-12 + + + + +Run the following command to install systemd-journal-remote +: + # dnf install systemd-journal-remote + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure systemd-journal-upload authentication is configured + + +Journald systemd-journal-upload + supports the ability to send log events it gathers to a remote log host. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + Storing log data on a remote host protects log integrity from local attacks. If an attacker gains root access on the local system, they could tamper with or remove log data that is stored on the local system. + + Note: + This recommendation +only applies if journald + is the chosen method for client side logging +. Do not apply this recommendation if rsyslog + is used. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12 + + + + +Edit the /etc/systemd/journal-upload.conf + file or a file in /etc/systemd/journal-upload.conf.d + ending in .conf + and ensure the following lines are set in the [Upload] + section per your environment: + +[Upload]
+URL=192.168.50.42
+ServerKeyFile=/etc/ssl/private/journal-upload.pem
+ServerCertificateFile=/etc/ssl/certs/journal-upload.pem
+TrustedCertificateFile=/etc/ssl/ca/trusted.pem +
+ Restart the service: + # systemctl restart systemd-journal-upload + +
+
+
+
+ + Ensure systemd-journal-upload is enabled and active + + +Journald systemd-journal-upload + supports the ability to send log events it gathers to a remote log host. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + Storing log data on a remote host protects log integrity from local attacks. If an attacker gains root access on the local system, they could tamper with or remove log data that is stored on the local system. + + Note: + This recommendation +only applies if journald + is the chosen method for client side logging +. Do not apply this recommendation if rsyslog + is used. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12 + + + + +Run the following commands to unmask, enable and start systemd-journal-upload +: + +# systemctl unmask systemd-journal-upload.service
+# systemctl --now enable systemd-journal-upload.service +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure systemd-journal-remote service is not in use + + +Journald systemd-journal-remote + supports the ability to receive messages from remote hosts, thus acting as a log server. Clients should not receive data from other hosts. + + Note: + + + +The same package, systemd-journal-remote +, is used for both sending logs to remote hosts and receiving incoming logs. + +With regards to receiving logs, there are two services; systemd-journal-remote.socket + and systemd-journal-remote.service +. + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If a client is configured to also receive data, thus turning it into a server, the client system is acting outside it's operational boundary. + + Note: + This recommendation +only applies if journald + is the chosen method for client side logging +. Do not apply this recommendation if rsyslog + is used. + + + + NIST SP 800-53 Rev. 5: AU-2, AU-7 AU-12 + + + + +Run the following commands to stop and mask systemd-journal-remote.socket + and systemd-journal-remote.service: + +# systemctl stop systemd-journal-remote.socket systemd-journal-remote.service
+# systemctl mask systemd-journal-remote.socket systemd-journal-remote.service +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + Ensure journald ForwardToSyslog is disabled + + +Data from journald + should be kept in the confines of the service and not forwarded to other services. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + + + - IF - + journald + is the method for capturing logs, all logs of the system should be handled by journald + and not forwarded to other logging mechanisms. + + Note: + This recommendation +only applies if journald + is the chosen method for client side logging +. Do not apply this recommendation if rsyslog + is used. + + + + NIST SP 800-53 Rev. 5: AU-2, AU-6, AU-7, AU-12 + + + + + - IF - + rsyslog + is the preferred method for capturing logs, this section and Recommendation should be skipped and the "Configure rsyslog" section followed. + + - IF - + journald + is the preferred method for capturing logs: + +Set the following parameter in the [Journal] + section in /etc/systemd/journald.conf + or a file in /etc/systemd/journald.conf.d/ ending in .conf +: + ForwardToSyslog=no + + + Example: + + +#!/usr/bin/env bash
+
+{
+ [ ! -d /etc/systemd/journald.conf.d/ ] && mkdir /etc/systemd/journald.conf.d/
+ if grep -Psq -- '^\h*\[Journal\]' /etc/systemd/journald.conf.d/60-journald.conf; then
+ printf '%s\n' "ForwardToSyslog=no" >> /etc/systemd/journald.conf.d/60-journald.conf
+ else
+ printf '%s\n' "[Journal]" "ForwardToSyslog=no" >> /etc/systemd/journald.conf.d/60-journald.conf
+ fi
+} +
+ + Note: + If this setting appears in a canonically later file, or later in the same file, the setting will be overwritten + Run to following command to update the parameters in the service: + # systemctl reload-or-restart systemd-journald + +
+
+
+ + + + + + + + + + + + + + + + + +
+ + Ensure journald Compress is configured + + The journald system includes the capability of compressing overly large files to avoid filling up the system with logs or making the logs unmanageably large. + + + + + + + Network + Detect + + + + Network + Protect + + + + + + Network + Detect + + + + Network + Detect + + + + Network + Detect + + + + + + Uncompressed large files may unexpectedly fill a filesystem leading to resource unavailability. Compressing logs prior to write can prevent sudden, unexpected filesystem impacts. + + Note: + This recommendation +only applies if journald + is the chosen method for client side logging +. Do not apply this recommendation if rsyslog + is used. + + + + + + + NIST SP 800-53 Rev. 5: AU-4 + + + + +Set the following parameter in the [Journal] + section in /etc/systemd/journald.conf + or a file in /etc/systemd/journald.conf.d/ + ending in .conf +: + Compress=yes + + + Example: + + +#!/usr/bin/env bash
+
+{
+ [ ! -d /etc/systemd/journald.conf.d/ ] && mkdir /etc/systemd/journald.conf.d/
+ if grep -Psq -- '^\h*\[Journal\]' /etc/systemd/journald.conf.d/60-journald.conf; then
+ printf '%s\n' "Compress=yes" >> /etc/systemd/journald.conf.d/60-journald.conf
+ else
+ printf '%s\n' "[Journal]" "Compress=yes" >> /etc/systemd/journald.conf.d/60-journald.conf
+ fi
+} +
+ + Note: + If this setting appears in a canonically later file, or later in the same file, the setting will be overwritten + Run to following command to update the parameters in the service: + # systemctl reload-or-restart systemd-journald + +
+
+
+ + + + + + + + + + + + + + + + + +
+ + Ensure journald Storage is configured + + Data from journald may be stored in volatile memory or persisted locally on the server. Logs in memory will be lost upon a system reboot. By persisting logs to local disk on the server they are protected from loss due to a reboot. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + Writing log data to disk will provide the ability to forensically reconstruct events which may have impacted the operations or security of a system even after a system crash or reboot. + + Note: + This recommendation +only applies if journald + is the chosen method for client side logging +. Do not apply this recommendation if rsyslog + is used. + + + + + NIST SP 800-53 Rev. 5: AU-3, AU-12 + + + + +Set the following parameter in the [Journal] + section in /etc/systemd/journald.conf + or a file in /etc/systemd/journald.conf.d/ + ending in .conf +: + Storage=persistent + + + Example: + + +#!/usr/bin/env bash
+
+{
+ [ ! -d /etc/systemd/journald.conf.d/ ] && mkdir /etc/systemd/journald.conf.d/
+ if grep -Psq -- '^\h*\[Journal\]' /etc/systemd/journald.conf.d/60-journald.conf; then
+ printf '%s\n' "Storage=persistent" >> /etc/systemd/journald.conf.d/60-journald.conf
+ else
+ printf '%s\n' "[Journal]" "Storage=persistent" >> /etc/systemd/journald.conf.d/60-journald.conf
+ fi
+} +
+ + Note: + If this setting appears in a canonically later file, or later in the same file, the setting will be overwritten + Run to following command to update the parameters in the service: + # systemctl reload-or-restart systemd-journald + +
+
+
+ + + + + + + + + + + + + + + + + +
+
+ + Configure rsyslog + + +The rsyslog + software package may be used instead of the default journald + logging mechanism. + Rsyslog has evolved over several decades. For this reason it supports three different configuration formats (“languages”): + + + basic + - previously known as the sysklogd + format, this is the format best used to express basic things, such as where the statement fits on a single line. + + It stems back to the original syslog.conf format, in use now for several decades. + The most common use case is matching on facility/severity and writing matching messages to a log file. + + + + advanced + - previously known as the RainerScript + format, this format was first available in rsyslog v6 and is the current, best and most precise format for non-trivial use cases where more than one line is needed. + + Prior to v7, there was a performance impact when using this format that encouraged use of the basic format for best results. Current versions of rsyslog do not suffer from this (historical) performance impact. + This new style format is specifically targeted towards more advanced use cases like forwarding to remote hosts that might be partially offline. + + + + obsolete legacy + - previously known simply as the legacy + format, this format is exactly what its name implies: it is obsolete and should not be used when writing new configurations. It was created in the early days (up to rsyslog version 5) where we expected that rsyslog would extend sysklogd just mildly. Consequently, it was primarily aimed at small additions to the original sysklogd format. + + Practice has shown that it was notoriously hard to use for more advanced use cases, and thus we replaced it with the advanced format. + In essence, everything that needs to be written on a single line that starts with a dollar sign is legacy format. Users of this format are encouraged to migrate to the basic or advanced formats. + + + + + Note: + This section only applies if rsyslog + is the chosen method for client side logging. Do not apply this section if journald + is used. + + + Ensure rsyslog is installed + + +The rsyslog + software is recommended in environments where journald + does not meet operation requirements. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + +The security enhancements of rsyslog + such as connection-oriented (i.e. TCP) transmission of logs, the option to log to database formats, and the encryption of log data en route to a central logging server) justify installing and configuring the package. + + Note: + This recommendation only applies if rsyslog + is the chosen method for client side logging. Do not apply this recommendation if journald + is used. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-3, AU-12 + + + + +Run the following command to install rsyslog +: + # dnf install rsyslog + + + + + + + + + + + + + + + + + Ensure rsyslog service is enabled and active + + +Once the rsyslog + package is installed, ensure that the service is enabled. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + +If the rsyslog + service is not enabled to start on boot, the system will not capture logging events. + + Note: + This recommendation only applies if rsyslog + is the chosen method for client side logging. Do not apply this recommendation if journald + is used. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-3, AU-12 + + + + + - IF - + rsyslog + is being used for logging on the system: + +Run the following commands to unmask, enable, and start rsyslog.service +: + +# systemctl unmask rsyslog.service
+# systemctl enable rsyslog.service
+# systemctl start rsyslog.service +
+
+
+
+ + + + + + + + + + + + + + + + + +
+ + Ensure journald is configured to send logs to rsyslog + + +Data from systemd-journald + may be stored in volatile memory or persisted locally on the server. Utilities exist to accept remote export of systemd-journald + logs, however, use of the rsyslog + service provides a consistent means of log collection and export. + + + + + + + Network + Detect + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + Network + Detect + + + + + +As noted in the systemd-journald + man pages, systemd-journald + logs may be exported to rsyslog + either through the process mentioned here, or through a facility like systemd-journald.service +. There are trade-offs involved in each implementation, where ForwardToSyslog + will immediately capture all events (and forward to an external log server, if properly configured), but may not capture all boot-up activities. Mechanisms such as systemd-journald.service +, on the other hand, will record bootup events, but may delay sending the information to rsyslog +, leading to the potential for log manipulation prior to export. Be aware of the limitations of all tools employed to secure a system. + + + + + - IF - + rsyslog + is the preferred method for capturing logs, all logs of the system should be sent to it for further processing. + + + + + + + NIST SP 800-53 Rev. 5: AC-3, AU-2, AU-4, AU-12, MP-2 + SYSTEMD-JOURNALD.SERVICE(8) + JOURNALD.CONF(5) + + + + + - IF - + rsyslog + is the preferred method for capturing logs: + +Set the following parameter in the [Journal] + section in /etc/systemd/journald.conf + or a file in /etc/systemd/journald.conf.d/ + ending in .conf +: + ForwardToSyslog=yes + + + Example: + + +#!/usr/bin/env bash
+
+{
+ [ ! -d /etc/systemd/journald.conf.d/ ] && mkdir /etc/systemd/journald.conf.d/
+ if grep -Psq -- '^\h*\[Journal\]' /etc/systemd/journald.conf.d/60-journald.conf; then
+ printf '%s\n' "ForwardToSyslog=yes" >> /etc/systemd/journald.conf.d/60-journald.conf
+ else
+ printf '%s\n' "[Journal]" "ForwardToSyslog=yes" >> /etc/systemd/journald.conf.d/60-journald.conf
+ fi
+} +
+ + Note: + If this setting appears in a canonically later file, or later in the same file, the setting will be overwritten + Run to following command to update the parameters in the service: + +Restart systemd-journald.service +: + # systemctl reload-or-restart systemd-journald.service + + Impact: + + + - IF - + Journald + is the preferred method for capturing logs, this section and Recommendation should be skipped and the "Configure Journald" section followed. + +
+
+
+ + + + + + + + + + +
+ + Ensure rsyslog log file creation mode is configured + + + rsyslog + will create logfiles that do not already exist on the system. + +The $FileCreateMode + parameter allows you to specify the creation mode with which rsyslog + creates new files. If not specified, the value 0644 is used (which retains backward-compatibility with earlier releases). The value given must always be a 4-digit octal number, with the initial digit being zero. + Please note that the actual permission depend on rsyslogd’s process umask. + + $FileCreateMode + may be specified multiple times. If so, it specifies the creation mode for all selector lines that follow until the next $FileCreateMode parameter. Order of lines is vitally important. + + + + + + + Data + Protect + + + + Network + Detect + + + + + + Applications + Protect + + + + Network + Detect + + + + Network + Detect + + + + + + It is important to ensure that log files have the correct permissions to ensure that sensitive data is archived and protected. + + Note: + This recommendation only applies if rsyslog + is the chosen method for client side logging. Do not apply this recommendation if systemd-journald + is used. + + + + + + + RSYSLOG.CONF(5) + NIST SP 800-53 Rev. 5: AC-3, AC-6, MP-2 + https://www.rsyslog.com/doc/ + https://www.rsyslog.com/doc/configuration/action/rsconf1_filecreatemode.html#filecreatemode + + + + +Edit either /etc/rsyslog.conf + or a dedicated .conf + file in /etc/rsyslog.d/ + and set $FileCreateMode + to 0640 + or more restrictive: + $FileCreateMode 0640 + + Restart the service: + # systemctl restart rsyslog + + + + + + + + + + + + + + + + + + + + Ensure rsyslog logging is configured + + +The /etc/rsyslog.conf + and /etc/rsyslog.d/*.conf + files specifies rules for logging and which files are to be used to log certain classes of messages. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + +A great deal of important security-related information is sent via rsyslog + (e.g., successful and failed su attempts, failed login attempts, root login attempts, etc.). + + Note: + This recommendation only applies if rsyslog + is the chosen method for client side logging. Do not apply this recommendation if journald + is used. + + + + + See the rsyslog.conf(5) man page for more information. + NIST SP 800-53 Rev. 5: AU-2, AU-7, AU-12 + + + + +Edit the following lines in the /etc/rsyslog.conf + and /etc/rsyslog.d/*.conf + files as appropriate for your environment. + + Note: + The below configuration is shown for example purposes only. Due care should be given to how the organization wishes to store log data. + +*.emerg :omusrmsg:*
+auth,authpriv.* /var/log/secure
+mail.* -/var/log/mail
+mail.info -/var/log/mail.info
+mail.warning -/var/log/mail.warn
+mail.err /var/log/mail.err
+cron.* /var/log/cron
+*.=warning;*.=err -/var/log/warn
+*.crit /var/log/warn
+*.*;mail.none;news.none -/var/log/messages
+local0,local1.* -/var/log/localmessages
+local2,local3.* -/var/log/localmessages
+local4,local5.* -/var/log/localmessages
+local6,local7.* -/var/log/localmessages +
+ +Run the following command to reload the rsyslogd + configuration: + # systemctl restart rsyslog + +
+
+
+
+ + Ensure rsyslog is configured to send logs to a remote log host + + + rsyslog + supports the ability to send log events it gathers to a remote log host or to receive messages from remote hosts, thus enabling centralized log management. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + +In addition, see the rsyslog documentation + for implementation details of TLS. + + + + Storing log data on a remote host protects log integrity from local attacks. If an attacker gains root access on the local system, they could tamper with or remove log data that is stored on the local system. + + Note: + This recommendation only applies if rsyslog + is the chosen method for client side logging. Do not apply this recommendation if systemd-journald + is used. + + + + + See the rsyslog.conf(5) man page for more information. + NIST SP 800-53 Rev. 5: AU-6 + https://www.rsyslog.com/doc/ + + + + +Edit the /etc/rsyslog.conf + and /etc/rsyslog.d/*.conf + files and add the following line (where loghost.example.com + is the name of your central log host). The target + directive may either be a fully qualified domain name or an IP address. + + Example: + + +*.* action(type="omfwd" target="loghost.example.com" port="514" protocol="tcp"
+ action.resumeRetryCount="100"
+ queue.type="LinkedList" queue.size="1000") +
+ +Run the following command to reload rsyslog.service +: + # systemctl reload-or-restart rsyslog.service + +
+
+
+
+ + Ensure rsyslog is not configured to receive logs from a remote client + + + rsyslog + supports the ability to receive messages from remote hosts, thus acting as a log server. Clients should not receive data from other hosts. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If a client is configured to also receive data, thus turning it into a server, the client system is acting outside its operational boundary. + + Note: + This recommendation only applies if rsyslog + is the chosen method for client side logging. Do not apply this recommendation if systemd-journald + is used. + + + + NIST SP 800-53 Rev. 5: AU-2, AU-7, AU-12, CM-6 + https://www.rsyslog.com/doc/index.html + + + + +Should there be any active log server configuration found in the auditing section, modify those files and remove the specific lines highlighted by the audit. Verify none of the following entries are present in any of /etc/rsyslog.conf + or /etc/rsyslog.d/*.conf +. + + advanced format + + +module(load="imtcp")
+input(type="imtcp" port="514") +
+ + deprecated legacy format + + +$ModLoad imtcp
+$InputTCPServerRun +
+ Restart the service: + # systemctl restart rsyslog + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure rsyslog logrotate is configured + + +The system includes the capability of rotating log files regularly to avoid filling up the system with logs or making the logs unmanageably large. The file /etc/logrotate.d/rsyslog + is the configuration file used to rotate log files created by rsyslog +. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +If no maxage + setting is set for logrotate + a situation can occur where logrotate + is interrupted and fails to delete rotated log files. It is recommended to set this to a value greater than the longest any log file should exist on your system to ensure that any such log file is removed but standard rotation settings are not overridden. + + + + By keeping the log files smaller and more manageable, a system administrator can easily archive these files to another system and spend less time looking through inordinately large log files. + + Note: + This recommendation only applies if rsyslog + is the chosen method for client side logging. Do not apply this recommendation if systemd-journald + is used. + + + + NIST SP 800-53 Rev. 5: AU-8 + https://www.rsyslog.com/doc/tutorials/log_rotation_fix_size.html + + + + +Edit /etc/logrotate.conf + and /etc/logrotate.d/* + to ensure logs are rotated according to site policy. + + Example logrotate configuration that specifies log files be rotated weekly, keep 4 backlogs, compress old log files, ignores missing and empty log files, postrotate to reload rsyslog service after logs are rotated + + +/var/log/rsyslog/*.log {
+ weekly
+ rotate 4
+ compress
+ missingok
+ notifempty
+ postrotate
+ /usr/bin/systemctl reload rsyslog.service >/dev/null || true
+ endscript
+} +
+
+
+
+
+
+ + Configure Logfiles + + + Ensure access to all logfiles has been configured + + Log files contain information from many services on the the local system, or in the event of a centralized log server, others systems logs as well. + +In general log files are found in /var/log/ +, although application can be configured to store logs elsewhere. Should your application store logs in another, ensure to run the same test on that location. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + It is important that log files have the correct permissions to ensure that sensitive data is protected and that only the appropriate users / groups have access to them. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following script to update permissions and ownership on files in /var/log +. + Although the script is not destructive, ensure that the output is captured in the event that the remediation causes issues. + +#!/usr/bin/env bash
+
+{
+ l_op2="" l_output2=""
+ l_uidmin="$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)"
+ file_test_fix()
+ {
+ l_op2=""
+ l_fuser="root"
+ l_fgroup="root"
+ if [ $(( $l_mode & $perm_mask )) -gt 0 ]; then
+ l_op2="$l_op2\n - Mode: \"$l_mode\" should be \"$maxperm\" or more restrictive\n - Removing excess permissions"
+ chmod "$l_rperms" "$l_fname"
+ fi
+ if [[ ! "$l_user" =~ $l_auser ]]; then
+ l_op2="$l_op2\n - Owned by: \"$l_user\" and should be owned by \"${l_auser//|/ or }\"\n - Changing ownership to: \"$l_fuser\""
+ chown "$l_fuser" "$l_fname"
+ fi
+ if [[ ! "$l_group" =~ $l_agroup ]]; then
+ l_op2="$l_op2\n - Group owned by: \"$l_group\" and should be group owned by \"${l_agroup//|/ or }\"\n - Changing group ownership to: \"$l_fgroup\""
+ chgrp "$l_fgroup" "$l_fname"
+ fi
+ [ -n "$l_op2" ] && l_output2="$l_output2\n - File: \"$l_fname\" is:$l_op2\n"
+ }
+ unset a_file && a_file=() # clear and initialize array
+ # Loop to create array with stat of files that could possibly fail one of the audits
+ while IFS= read -r -d $'\0' l_file; do
+ [ -e "$l_file" ] && a_file+=("$(stat -Lc '%n^%#a^%U^%u^%G^%g' "$l_file")")
+ done < <(find -L /var/log -type f \( -perm /0137 -o ! -user root -o ! -group root \) -print0)
+ while IFS="^" read -r l_fname l_mode l_user l_uid l_group l_gid; do
+ l_bname="$(basename "$l_fname")"
+ case "$l_bname" in
+ lastlog | lastlog.* | wtmp | wtmp.* | wtmp-* | btmp | btmp.* | btmp-* | README)
+ perm_mask='0113'
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_rperms="ug-x,o-wx"
+ l_auser="root"
+ l_agroup="(root|utmp)"
+ file_test_fix
+ ;;
+ secure | auth.log | syslog | messages)
+ perm_mask='0137'
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_rperms="u-x,g-wx,o-rwx"
+ l_auser="(root|syslog)"
+ l_agroup="(root|adm)"
+ file_test_fix
+ ;;
+ SSSD | sssd)
+ perm_mask='0117'
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_rperms="ug-x,o-rwx"
+ l_auser="(root|SSSD)"
+ l_agroup="(root|SSSD)"
+ file_test_fix
+ ;;
+ gdm | gdm3)
+ perm_mask='0117'
+ l_rperms="ug-x,o-rwx"
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_auser="root"
+ l_agroup="(root|gdm|gdm3)"
+ file_test_fix
+ ;;
+ *.journal | *.journal~)
+ perm_mask='0137'
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_rperms="u-x,g-wx,o-rwx"
+ l_auser="root"
+ l_agroup="(root|systemd-journal)"
+ file_test_fix
+ ;;
+ *)
+ perm_mask='0137'
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_rperms="u-x,g-wx,o-rwx"
+ l_auser="(root|syslog)"
+ l_agroup="(root|adm)"
+ if [ "$l_uid" -lt "$l_uidmin" ] && [ -z "$(awk -v grp="$l_group" -F: '$1==grp {print $4}' /etc/group)" ]; then
+ if [[ ! "$l_user" =~ $l_auser ]]; then
+ l_auser="(root|syslog|$l_user)"
+ fi
+ if [[ ! "$l_group" =~ $l_agroup ]]; then
+ l_tst=""
+ while l_out3="" read -r l_duid; do
+ [ "$l_duid" -ge "$l_uidmin" ] && l_tst=failed
+ done <<< "$(awk -F: '$4=='"$l_gid"' {print $3}' /etc/passwd)"
+ [ "$l_tst" != "failed" ] && l_agroup="(root|adm|$l_group)"
+ fi
+ fi
+ file_test_fix
+ ;;
+ esac
+ done <<< "$(printf '%s\n' "${a_file[@]}")"
+ unset a_file # Clear array
+ # If all files passed, then we report no changes
+ if [ -z "$l_output2" ]; then
+ echo -e "- All files in \"/var/log/\" have appropriate permissions and ownership\n - No changes required\n"
+ else
+ # print report of changes
+ echo -e "\n$l_output2"
+ fi
+} +
+ + Note: + You may also need to change the configuration for your logging software or services for any logs that had incorrect permissions. + If there are services that log to other locations, ensure that those log files have the appropriate access configured. +
+
+
+ + + + + + +
+
+
+ + System Auditing + + +The Linux Auditing System operates on a set of rules that collects certain types of system activity to facilitate incident investigation, detect unauthorized access or modification of data. By default events will be logged to /var/log/audit/audit.log +, which can be configured in /etc/audit/auditd.conf +. + The following types of audit rules can be specified: + + Control rules: Configuration of the auditing system. + File system rules: Allow the auditing of access to a particular file or a directory. Also known as file watches. + System call rules: Allow logging of system calls that any specified program makes. + + Audit rules can be set: + + +On the command line using the auditctl + utility. These rules are not persistent across reboots. + +In /etc/audit/audit.rules +. These rules have to be merged and loaded before they are active. + + + Notes: + + + +For 64 bit systems that have arch + as a rule parameter, you will need two rules: one for 64 bit and one for 32 bit systems calls. For 32 bit systems, only one rule is needed. + +If the auditing system is configured to be locked ( -e 2 +), a system reboot will be required in order to load any changes. + Key names are optional on the rules and will not be used in compliance auditing. The usage of key names is highly recommended as it facilitates organization and searching; as such, all remediation steps will have key names supplied. + +It is best practice to store the rules, in number prepended files, in /etc/audit/rules.d/ +. Rules must end in a .rules + suffix. This then requires the use of augenrules + to merge all the rules into /etc/audit/audit.rules + based on their alphabetical (lexical) sort order. All benchmark recommendations follow this best practice for remediation, specifically using the prefix of 50 + which is center weighed if all rule sets make use of the number prepending naming convention. + +Your system may have been customized to change the default UID_MIN +. All sample output uses 1000 +, but this value will not be used in compliance auditing. To confirm the UID_MIN + for your system, run the following command: awk '/^\s*UID_MIN/{print $2}' /etc/login.defs + + + + Normalization + +The Audit system normalizes some entries, so when you look at the sample output keep in mind that: + + +With regards to users whose login UID is not set, the values -1 + / unset + / 4294967295 + are equivalent and normalized to -1 +. + +When comparing field types and both sides of the comparison is valid fields types, such as euid!=uid +, then the auditing system may normalize such that the output is uid!=euid +. + Some parts of the rule may be rearranged whilst others are dependent on previous syntax. For example, the following two statements are the same: + + -a always,exit -F arch=b64 -S execve -C uid!=euid -F auid!=-1 -F key=user_emulation + + and + -a always,exit -F arch=b64 -C euid!=uid -F auid!=unset -S execve -k user_emulation + + + Capacity planning + + The recommendations in this section implement auditing policies that not only produce large quantities of logged data, but may also negatively impact system performance. Capacity planning is critical in order not to adversely impact production environments. + + Disk space. If a significantly large set of events are captured, additional on system or off system storage may need to be allocated. If the logs are not sent to a remote log server, ensure that log rotation is implemented else the disk will fill up and the system will halt. Even when logs are sent to a log server, ensure sufficient disk space to allow caching of logs in the case of temporary network outages. + Disk IO. It is not just the amount of data collected that should be considered, but the rate at which logs are generated. + CPU overhead. System call rules might incur considerable CPU overhead. Test the systems open/close syscalls per second with and without the rules to gauge the impact of the rules. + + + + Configure auditd Service + + The capturing of system events provides system administrators with information to allow them to determine if unauthorized access to their system is occurring. + + + Ensure auditd packages are installed + + + auditd + is the userspace component to the Linux Auditing System. It's responsible for writing audit records to the disk. + + + + + + + Network + Detect + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + The capturing of system events provides system administrators with information to allow them to determine if unauthorized access to their system is occurring. + + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-3, AU-3(1), AU-12, SI-5 + + + + +Run the following command to install audit + and audit-libs +: + # dnf install audit audit-libs + + + + + + + + + + + + + + + Ensure auditing for processes that start prior to auditd is enabled + + +Configure grub2 + so that processes that are capable of being audited can be audited even if they start up prior to auditd + startup. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + This recommendation is designed around the grub 2 bootloader, if another bootloader is in use in your environment enact equivalent settings. + + grubby + is a command line tool used to configure bootloader menu entries across multiple architectures. It is used for updating and displaying information about the configuration files for various architecture specific bootloaders. + It is primarily designed to be used from scripts which install new kernels and need to find information about the current boot environment. + The grubby executable has full support for the grub2 bootloader on x86_64 systems using legacy BIOS or modern UEFI firmware and ppc64 and ppc64le hardware using OPAL or SLOF as firmware. + Legacy s390 and the current s390x architectures and their zipl bootloader are fully supported. + Support for yaboot has been deprecated as all ppc architecture hardware since the Power8 uses grub2 or petitboot which both use the grub2 configuration file format. + Legacy bootloaders LILO, SILO, and ELILO are deprecated and no longer receiving active support in favor of previously mentioned bootloaders. + The default bootloader target is primarily determined by the architecture for which grubby has been built. Each architecture has a preferred bootloader, and each bootloader has its own configuration file. If no bootloader is selected on the command line, grubby will use these default settings to search for an existing configuration. If no bootloader configuration file is found, grubby will use the default value for that architecture. + + + + +Audit events need to be captured on processes that start up prior to auditd + , so that potential malicious activity cannot go undetected. + + + + + + + +Run the following command to update the grub2 + configuration with audit=1 +: + # grubby --update-kernel ALL --args 'audit=1' + + +Edit /etc/default/grub + and add audit=1 + to the GRUB_CMDLINE_LINUX= + line between the opening and closing double quotes: + + Example: + + GRUB_CMDLINE_LINUX="quiet audit=1" + + + Note: + Other parameters may also be listed + + + + + + + + + + + + + + + + Ensure audit_backlog_limit is sufficient + + +The audit_backlog_limit + parameter determines how auditd records can be held in the auditd backlog. The default setting of 64 may be insufficient to store all audit events during boot. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + This recommendation is designed around the grub 2 bootloader, if another bootloader is in use in your environment enact equivalent settings. + + grubby + is a command line tool used to configure bootloader menu entries across multiple architectures. It is used for updating and displaying information about the configuration files for various architecture specific bootloaders. + It is primarily designed to be used from scripts which install new kernels and need to find information about the current boot environment. + The grubby executable has full support for the grub2 bootloader on x86_64 systems using legacy BIOS or modern UEFI firmware and ppc64 and ppc64le hardware using OPAL or SLOF as firmware. + Legacy s390 and the current s390x architectures and their zipl bootloader are fully supported. + Support for yaboot has been deprecated as all ppc architecture hardware since the Power8 uses grub2 or petitboot which both use the grub2 configuration file format. + Legacy bootloaders LILO, SILO, and ELILO are deprecated and no longer receiving active support in favor of previously mentioned bootloaders. + The default bootloader target is primarily determined by the architecture for which grubby has been built. Each architecture has a preferred bootloader, and each bootloader has its own configuration file. If no bootloader is selected on the command line, grubby will use these default settings to search for an existing configuration. If no bootloader configuration file is found, grubby will use the default value for that architecture. + + + + +During boot if audit=1 +, then the backlog will hold 64 records. If more than 64 records are created during boot, auditd records will be lost and potential malicious activity could go undetected. + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + +Run the following command to add audit_backlog_limit=<BACKLOG SIZE> + to GRUB_CMDLINE_LINUX: + # grubby --update-kernel ALL --args 'audit_backlog_limit=<BACKLOG SIZE>' + + + Example: + + # grubby --update-kernel ALL --args 'audit_backlog_limit=8192' + + +Edit /etc/default/grub + and add audit_backlog_limit=<BACKLOG SIZE> + to the GRUB_CMDLINE_LINUX= + line between the opening and closing double quotes: + + Example: + + GRUB_CMDLINE_LINUX="quiet audit_backlog_limit=8192" + + + Note: + Other parameters may also be listed + + + + + + + + + + + + + + + + Ensure auditd service is enabled and active + + +Turn on the auditd + daemon to record system events. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + Additional methods of enabling a service exist. Consult your distribution documentation for appropriate methods. + + + + The capturing of system events provides system administrators with information to allow them to determine if unauthorized access to their system is occurring. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + +Run the following commands to unmask, enable and start auditd +: + +# systemctl unmask auditd
+# systemctl enable auditd
+# systemctl start auditd +
+
+
+
+ + + + + + + + + + +
+
+ + Configure Data Retention + + When auditing, it is important to carefully configure the storage requirements for audit logs. By default, auditd will max out the log files at 5MB and retain only 4 copies of them. Older versions will be deleted. It is possible on a system that the 20 MBs of audit logs may fill up the system causing loss of audit data. While the recommendations here provide guidance, check your site policy for audit storage requirements. + + + Ensure audit log storage size is configured + + Configure the maximum size of the audit log file. Once the log reaches the maximum size, it will be rotated and a new log file will be started. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +The max_log_file + parameter is measured in megabytes. + +Other methods of log rotation may be appropriate based on site policy. One example is time-based rotation strategies which don't have native support in auditd + configurations. Manual audit of custom configurations should be evaluated for effectiveness and completeness. + + + + It is important that an appropriate size is determined for log files so that they do not impact the system and audit data is not lost. + + + + NIST SP 800-53 Rev. 5: AU-8 + + + + +Set the following parameter in /etc/audit/auditd.conf + in accordance with site policy: + max_log_file = <MB> + + + + + + + + + + + + Ensure audit logs are not automatically deleted + + +The max_log_file_action + setting determines how to handle the audit log file reaching the max file size. A value of keep_logs + will rotate the logs but never delete old logs. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + + In high security contexts, the benefits of maintaining a long audit history exceed the cost of storing the audit history. + + + + NIST SP 800-53 Rev. 5: AU-8 + + + + +Set the following parameter in /etc/audit/auditd.conf: + + max_log_file_action = keep_logs + + + + + + + + + + + + Ensure system is disabled when audit logs are full + + +The auditd + daemon can be configured to halt the system or put the system in single user mode, if no free space is available or an error is detected on the partition that holds the audit log files. + +The disk_full_action + parameter tells the system what action to take when no free space is available on the partition that holds the audit log files. Valid values are ignore +, syslog +, rotate +, exec +, suspend +, single +, and halt +. + + + ignore +, the audit daemon will issue a syslog message but no other action is taken + + syslog +, the audit daemon will issue a warning to syslog + + rotate +, the audit daemon will rotate logs, losing the oldest to free up space + + exec +, /path-to-script will execute the script. You cannot pass parameters to the script. The script is also responsible for telling the auditd daemon to resume logging once its completed its action + + suspend +, the audit daemon will stop writing records to the disk + + single +, the audit daemon will put the computer system in single user mode + + halt +, the audit daemon will shut down the system + + +The disk_error_action + parameter tells the system what action to take when an error is detected on the partition that holds the audit log files. Valid values are ignore +, syslog +, exec +, suspend +, single +, and halt +. + + + ignore +, the audit daemon will not take any action + + syslog +, the audit daemon will issue no more than 5 consecutive warnings to syslog + + exec +, /path-to-script will execute the script. You cannot pass parameters to the script + + suspend +, the audit daemon will stop writing records to the disk + + single +, the audit daemon will put the computer system in single user mode + + halt +, the audit daemon will shut down the system + + + + + + + + Network + Detect + + + + Network + Protect + + + + + + In high security contexts, the risk of detecting unauthorized access or nonrepudiation exceeds the benefit of the system's availability. + + + + NIST SP 800-53 Rev. 5: AU-2, AU-8, AU-12, SI-5 + AUDITD.CONF(5) + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/auditing-the-system_security-hardening#configuring-auditd-for-a-secure-environment_auditing-the-system + + + + +Set one of the following parameters in /etc/audit/auditd.conf + depending on your local security policies. + +disk_full_action = <halt|single>
+disk_error_action = <syslog|single|halt> +
+ + Example: + + +disk_full_action = halt
+disk_error_action = halt +
+ Impact: + + + disk_full_action + parameter: + + +Set to halt + - the auditd + daemon will shutdown the system when the disk partition containing the audit logs becomes full. + +Set to single + - the auditd + daemon will put the computer system in single user mode when the disk partition containing the audit logs becomes full. + + + disk_error_action + parameter: + + +Set to halt + - the auditd + daemon will shutdown the system when an error is detected on the partition that holds the audit log files. + +Set to single + - the auditd + daemon will put the computer system in single user mode when an error is detected on the partition that holds the audit log files. + +Set to syslog + - the auditd + daemon will issue no more than 5 consecutive warnings to syslog when an error is detected on the partition that holds the audit log files. + + +
+
+
+ + + + + + + + +
+ + Ensure system warns when audit logs are low on space + + +The auditd + daemon can be configured to halt the system, put the system in single user mode or send a warning message, if the partition that holds the audit log files is low on space. + +The space_left_action + parameter tells the system what action to take when the system has detected that it is starting to get low on disk space. Valid values are ignore +, syslog +, rotate +, email +, exec +, suspend +, single +, and halt +. + + + ignore +, the audit daemon does nothing + + syslog +, the audit daemon will issue a warning to syslog + + rotate +, the audit daemon will rotate logs, losing the oldest to free up space + + email +, the audit daemon will send a warning to the email account specified in action_mail_acct + as well as sending the message to syslog + + exec +, /path-to-script will execute the script. You cannot pass parameters to the script. The script is also responsible for telling the auditd daemon to resume logging once its completed its action + + suspend +, the audit daemon will stop writing records to the disk + + single +, the audit daemon will put the computer system in single user mode + + halt +, the audit daemon will shut down the system + + +The admin_space_left_action + parameter tells the system what action to take when the system has detected that it is low on disk space. Valid values are ignore +, syslog +, rotate +, email +, exec +, suspend +, single +, and halt +. + + + ignore +, the audit daemon does nothing + + syslog +, the audit daemon will issue a warning to syslog + + rotate +, the audit daemon will rotate logs, losing the oldest to free up space + + email +, the audit daemon will send a warning to the email account specified in action_mail_acct + as well as sending the message to syslog + + exec +, /path-to-script will execute the script. You cannot pass parameters to the script. The script is also responsible for telling the auditd daemon to resume logging once its completed its action + + suspend +, the audit daemon will stop writing records to the disk + + single +, the audit daemon will put the computer system in single user mode + + halt +, the audit daemon will shut down the system + + + + + + + + Network + Detect + + + + Network + Protect + + + + + + In high security contexts, the risk of detecting unauthorized access or nonrepudiation exceeds the benefit of the system's availability. + + + + NIST SP 800-53 Rev. 5: AU-2, AU-8, AU-12, SI-5 + AUDITD.CONF(5) + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/auditing-the-system_security-hardening#configuring-auditd-for-a-secure-environment_auditing-the-system + + + + +Set the space_left_action + parameter in /etc/audit/auditd.conf + to email +, exec +, single +, or halt +: + + Example: + + space_left_action = email + + +Set the admin_space_left_action + parameter in /etc/audit/auditd.conf + to single + or halt +: + + Example: + + admin_space_left_action = single + + + Note: + A Mail Transfer Agent (MTA) must be installed and configured properly to set space_left_action = email + + Impact: + + +If the admin_space_left_action + is set to single + the audit daemon will put the computer system in single user mode. + + + + + + + + + + + + + +
+ + Configure auditd Rules + + The Audit system operates on a set of rules that define what is to be captured in the log files. + The following types of Audit rules can be specified: + + Control rules: Allow the Audit system's behavior and some of its configuration to be modified. + File system rules: Allow the auditing of access to a particular file or a directory. (Also known as file watches) + System call rules: Allow logging of system calls that any specified program makes. + + Audit rules can be set: + + on the command line using the auditctl utility. Note that these rules are not persistent across reboots. + +in a file ending in .rules + in the /etc/audit/rules.d/ + directory. + + + + Ensure changes to system administration scope (sudoers) is collected + + +Monitor scope changes for system administrators. If the system has been properly configured to force system administrators to log in as themselves first and then use the sudo + command to execute privileged commands, it is possible to monitor changes in scope. The file /etc/sudoers +, or files in /etc/sudoers.d +, will be written to when the file(s) or related attributes have changed. The audit records will be tagged with the identifier "scope". + + + + + + + Network + Detect + + + + + + Users + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +Changes in the /etc/sudoers + and /etc/sudoers.d + files can indicate that an unauthorized change has been made to the scope of system administrator activity. + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor scope changes for system administrators. + + Example: + + # printf '%s\n' "-w /etc/sudoers -p wa -k scope" "-w /etc/sudoers.d -p wa -k scope" >> /etc/audit/rules.d/50-scope.rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + + + + + + + + + + + + + + + + + + Ensure actions as another user are always logged + + + sudo + provides users with temporary elevated privileges to perform operations, either as the superuser or another user. + + + + + + + Network + Detect + + + + + + Users + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +Creating an audit log of users with temporary elevated privileges and the operation(s) they performed is essential to reporting. Administrators will want to correlate the events written to the audit trail with the records written to sudo +'s logfile to verify if unauthorized commands have been executed. + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor elevated privileges. + + Example: + + +# printf "
+-a always,exit -F arch=b64 -C euid!=uid -F auid!=unset -S execve -k user_emulation
+-a always,exit -F arch=b32 -C euid!=uid -F auid!=unset -S execve -k user_emulation
+" >> /etc/audit/rules.d/50-user_emulation.rules +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + +
+ + Ensure events that modify the sudo log file are collected + + +Monitor the sudo + log file. If the system has been properly configured to disable the use of the su + command and force all administrators to have to log in first and then use sudo + to execute privileged commands, then all administrator commands will be logged to /var/log/sudo.log + . Any time a command is executed, an audit event will be triggered as the /var/log/sudo.log + file will be opened for write and the executed administration command will be written to the log. + + + + + + + Network + Detect + + + + + + Users + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +Changes in /var/log/sudo.log + indicate that an administrator has executed a command or the log file itself has been tampered with. Administrators will want to correlate the events written to the audit trail with the records written to /var/log/sudo.log + to verify if unauthorized commands have been executed. + + + + + + + + Note: + This recommendation requires that the sudo logfile is configured. See guidance provided in the recommendation "Ensure sudo log file exists" + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor events that modify the sudo log file. + + Example: + + +# {
+SUDO_LOG_FILE=$(grep -r logfile /etc/sudoers* | sed -e 's/.*logfile=//;s/,? .*//' -e 's/"//g')
+[ -n "${SUDO_LOG_FILE}" ] && printf "
+-w ${SUDO_LOG_FILE} -p wa -k sudo_log_file
+" >> /etc/audit/rules.d/50-sudo.rules || printf "ERROR: Variable 'SUDO_LOG_FILE' is unset.\n"
+} +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + +
+ + Ensure events that modify date and time information are collected + + Capture events where the system date and/or time has been modified. The parameters in this section are set to determine if the; + + + adjtimex + - tune kernel clock + + settimeofday + - set time using timeval + and timezone + structures + + stime + - using seconds since 1/1/1970 + + clock_settime + - allows for the setting of several internal clocks and timers + + system calls have been executed. Further, ensure to write an audit record to the configured audit log file upon exit, tagging the records with a unique identifier such as "time-change". + + + + + + + Network + Detect + + + + + + Applications + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Unexpected changes in system date and/or time could be a sign of malicious activity on the system. + + + + NIST SP 800-53 Rev. 5: AU-3, CM-6 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor events that modify date and time information. + Example: + +# printf "
+-a always,exit -F arch=b64 -S adjtimex,settimeofday -k time-change
+-a always,exit -F arch=b32 -S adjtimex,settimeofday -k time-change
+-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -k time-change
+-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -k time-change
+-w /etc/localtime -p wa -k time-change
+" >> /etc/audit/rules.d/50-time-change.rules +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure events that modify the system's network environment are collected + + Record changes to network environment files or system calls. The below parameters monitors the following system calls, and write an audit event on system call exit: + + + sethostname + - set the systems host name + + setdomainname + - set the systems domain name + + The files being monitored are: + + + /etc/issue + and /etc/issue.net + - messages displayed pre-login + + /etc/hosts + - file containing host names and associated IP addresses + + /etc/hostname + - file contains the system's host name + + /etc/sysconfig/network + - additional information that is valid to all network interfaces + + /etc/sysconfig/network-scripts/ + - directory containing network interface scripts and configurations files + + /etc/NetworkManager/ + - directory contains configuration files and settings used by the NetworkManager + + + + + + + + + Network + Detect + + + + + + Applications + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +Monitoring sethostname + and setdomainname + will identify potential unauthorized changes to host and domain name of a system. The changing of these names could potentially break security parameters that are set based on those names. The /etc/hosts + file is monitored for changes that can indicate an unauthorized intruder is trying to change machine associations with IP addresses and trick users and processes into connecting to unintended machines. Monitoring /etc/issue + and /etc/issue.net + is important, as intruders could put disinformation into those files and trick users into providing information to the intruder. Monitoring /etc/sysconfig/network + is important as it can show if network interfaces or scripts are being modified in a way that can lead to the machine becoming unavailable or compromised. All audit records should have a relevant tag associated with them. + + + + NIST SP 800-53 Rev. 5: AU-3, CM-6 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor events that modify the system's network environment. + Example: + +# printf "
+-a always,exit -F arch=b64 -S sethostname,setdomainname -k system-locale
+-a always,exit -F arch=b32 -S sethostname,setdomainname -k system-locale
+-w /etc/issue -p wa -k system-locale
+-w /etc/issue.net -p wa -k system-locale
+-w /etc/hosts -p wa -k system-locale
+-w /etc/hostname -p wa -k system-locale
+-w /etc/sysconfig/network -p wa -k system-locale
+-w /etc/sysconfig/network-scripts/ -p wa -k system-locale
+-w /etc/NetworkManager -p wa -k system-locale
+" >> /etc/audit/rules.d/50-system_locale.rules +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure use of privileged commands are collected + + +Monitor privileged programs, those that have the setuid + and/or setgid + bit set on execution, to determine if unprivileged users are running these commands. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Execution of privileged commands by non-privileged users could be an indication of someone trying to gain unauthorized access to the system. + + + + NIST SP 800-53 Rev. 5: AU-3, AU-3(1) + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor the use of privileged commands. + + Example script: + + +#!/usr/bin/env bash
+
+{
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ AUDIT_RULE_FILE="/etc/audit/rules.d/50-privileged.rules"
+ NEW_DATA=()
+ for PARTITION in $(findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) | grep -Pv "noexec|nosuid" | awk '{print $1}'); do
+ readarray -t DATA < <(find "${PARTITION}" -xdev -perm /6000 -type f | awk -v UID_MIN=${UID_MIN} '{print "-a always,exit -F path=" $1 " -F perm=x -F auid>="UID_MIN" -F auid!=unset -k privileged" }')
+ for ENTRY in "${DATA[@]}"; do
+ NEW_DATA+=("${ENTRY}")
+ done
+ done
+ readarray &> /dev/null -t OLD_DATA < "${AUDIT_RULE_FILE}"
+ COMBINED_DATA=( "${OLD_DATA[@]}" "${NEW_DATA[@]}" )
+ printf '%s\n' "${COMBINED_DATA[@]}" | sort -u > "${AUDIT_RULE_FILE}"
+} +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + + Special mount points + + +If there are any special mount points that are not visible by default from just scanning / +, change the PARTITION + variable to the appropriate partition and re-run the remediation. + Impact: + + +Both the audit and remediation section of this recommendation will traverse all mounted file systems that is not mounted with either noexec + or nosuid + mount options. If there are large file systems without these mount options, such traversal will be significantly detrimental to the performance of the system. + + Before running either the audit or remediation section, inspect the output of the following command to determine exactly which file systems will be traversed: + # findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) | grep -Pv "noexec|nosuid" + + +To exclude a particular file system due to adverse performance impacts, update the audit and remediation sections by adding a sufficiently unique string to the grep + statement. The above command can be used to test the modified exclusions. + +
+
+
+ + + + + + +
+ + Ensure unsuccessful file access attempts are collected + + Monitor for unsuccessful attempts to access files. The following parameters are associated with system calls that control files: + + +creation - creat + + +opening - open + , openat + + +truncation - truncate + , ftruncate + + + An audit log record will only be written if all of the following criteria is met for the user when trying to access a file: + + a non-privileged user (auid>=UID_MIN) + is not a Daemon event (auid=4294967295/unset/-1) + if the system call returned EACCES (permission denied) or EPERM (some other permanent error associated with the specific system call) + + + + + + + + Network + Detect + + + + + + Data + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Failed attempts to open, create or truncate files could be an indication that an individual or process is trying to gain unauthorized access to the system. + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor unsuccessful file access attempts. + + Example: + + +# {
+UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+[ -n "${UID_MIN}" ] && printf "
+-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>=${UID_MIN} -F auid!=unset -k access
+-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>=${UID_MIN} -F auid!=unset -k access
+-a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>=${UID_MIN} -F auid!=unset -k access
+-a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>=${UID_MIN} -F auid!=unset -k access
+" >> /etc/audit/rules.d/50-access.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure events that modify user/group information are collected + + Record events affecting the modification of user or group information, including that of passwords and old passwords if in use. + + + /etc/group + - system groups + + /etc/passwd + - system users + + /etc/gshadow + - encrypted password for each group + + /etc/shadow + - system user passwords + + /etc/security/opasswd + - storage of old passwords if the relevant PAM module is in use + + /etc/nsswitch.conf + - file configures how the system uses various databases and name resolution mechanisms + + /etc/pam.conf + - file determines the authentication services to be used, and the order in which the services are used. + + /etc/pam.d + - directory contains the PAM configuration files for each PAM-aware application. + + The parameters in this section will watch the files to see if they have been opened for write or have had attribute changes (e.g. permissions) and tag them with the identifier "identity" in the audit log file. + + + + + + + Network + Detect + + + + + + Users + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Unexpected changes to these files could be an indication that the system has been compromised and that an unauthorized user is attempting to hide their activities or compromise additional accounts. + + + + NIST SP 800-53 Rev. 5: AU-3 + https://manpages.debian.org/bookworm/manpages/nsswitch.conf.5.en.html + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/managing_smart_cards/pam_configuration_files + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor events that modify user/group information. + + Example: + + +# printf "
+-w /etc/group -p wa -k identity
+-w /etc/passwd -p wa -k identity
+-w /etc/gshadow -p wa -k identity
+-w /etc/shadow -p wa -k identity
+-w /etc/security/opasswd -p wa -k identity
+-w /etc/nsswitch.conf -p wa -k identity
+-w /etc/pam.conf -p wa -k identity
+-w /etc/pam.d -p wa -k identity
+" >> /etc/audit/rules.d/50-identity.rules +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure discretionary access control permission modification events are collected + + Monitor changes to file permissions, attributes, ownership and group. The parameters in this section track changes for system calls that affect file permissions and attributes. The following commands and system calls effect the permissions, ownership and various attributes of files. + + + chmod + + + fchmod + + + fchmodat + + + chown + + + fchown + + + fchownat + + + lchown + + + setxattr + + + lsetxattr + + + fsetxattr + + + removexattr + + + lremovexattr + + + fremovexattr + + + In all cases, an audit record will only be written for non-system user ids and will ignore Daemon events. All audit records will be tagged with the identifier "perm_mod." + + + + + + + Network + Detect + + + + + + Applications + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Monitoring for changes in file attributes could alert a system administrator to activity that could indicate intruder activity or policy violation. + + + + NIST SP 800-53 Rev. 5: AU-3, CM-6 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor discretionary access control permission modification events. + + Example: + + +# {
+UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+[ -n "${UID_MIN}" ] && printf "
+-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+-a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+" >> /etc/audit/rules.d/50-perm_mod.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + +
+ + Ensure successful file system mounts are collected + + +Monitor the use of the mount + system call. The mount + (and umount + ) system call controls the mounting and unmounting of file systems. The parameters below configure the system to create an audit record when the mount system call is used by a non-privileged user + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +It is highly unusual for a non privileged user to mount + file systems to the system. While tracking mount + commands gives the system administrator evidence that external media may have been mounted (based on a review of the source of the mount and confirming it's an external media type), it does not conclusively indicate that data was exported to the media. System administrators who wish to determine if data were exported, would also have to track successful open +, creat + and truncate + system calls requiring write access to a file under the mount point of the external media file system. This could give a fair indication that a write occurred. The only way to truly prove it, would be to track successful writes to the external media. Tracking write system calls could quickly fill up the audit log and is not recommended. Recommendations on configuration options to track data export to media is beyond the scope of this document. + + + + NIST SP 800-53 Rev. 5: CM-6 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor successful file system mounts. + + Example: + + +# {
+UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+[ -n "${UID_MIN}" ] && printf "
+-a always,exit -F arch=b32 -S mount -F auid>=$UID_MIN -F auid!=unset -k mounts
+-a always,exit -F arch=b64 -S mount -F auid>=$UID_MIN -F auid!=unset -k mounts
+" >> /etc/audit/rules.d/50-mounts.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + +
+ + Ensure session initiation information is collected + + Monitor session initiation events. The parameters in this section track changes to the files associated with session events. + + + /var/run/utmp + - tracks all currently logged in users. + + /var/log/wtmp + - file tracks logins, logouts, shutdown, and reboot events. + + /var/log/btmp + - keeps track of failed login attempts and can be read by entering the command /usr/bin/last -f /var/log/btmp +. + + All audit records will be tagged with the identifier "session." + + + + + + + Network + Detect + + + + + + Users + Detect + + + + Users + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Monitoring these files for changes could alert a system administrator to logins occurring at unusual hours, which could indicate intruder activity (i.e. a user logging in at a time when they do not normally log in). + + + + + NIST SP 800-53 Rev. 5: AU-3, AU-3 + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor session initiation information. + + Example: + + +# printf "
+-w /var/run/utmp -p wa -k session
+-w /var/log/wtmp -p wa -k session
+-w /var/log/btmp -p wa -k session
+" >> /etc/audit/rules.d/50-session.rules +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure login and logout events are collected + + Monitor login and logout events. The parameters below track changes to files associated with login/logout events. + + + /var/log/lastlog + - maintain records of the last time a user successfully logged in. + + /var/run/faillock + - directory maintains records of login failures via the pam_faillock + module. + + + + + + + + Network + Detect + + + + + + Users + Detect + + + + Users + Protect + + + + Users + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Monitoring login/logout events could provide a system administrator with information associated with brute force attacks against user logins. + + + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor login and logout events. + + Example: + + +# printf "
+-w /var/log/lastlog -p wa -k logins
+-w /var/run/faillock -p wa -k logins
+" >> /etc/audit/rules.d/50-login.rules +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + +
+ + Ensure file deletion events by users are collected + + Monitor the use of system calls associated with the deletion or renaming of files and file attributes. This configuration statement sets up monitoring for: + + + unlink + - remove a file + + unlinkat + - remove a file attribute + + rename + - rename a file + + renameat + rename a file attribute +system calls and tags them with the identifier "delete". + + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Monitoring these calls from non-privileged users could provide a system administrator with evidence that inappropriate removal of files and file attributes associated with protected files is occurring. While this audit option will look at all events, system administrators will want to look for specific privileged files that are being deleted or altered. + + + + NIST SP 800-53 Rev. 5: AU-12, SC-7 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor file deletion events by users. + + Example: + + +# {
+UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+[ -n "${UID_MIN}" ] && printf "
+-a always,exit -F arch=b64 -S rename,unlink,unlinkat,renameat -F auid>=${UID_MIN} -F auid!=unset -F key=delete
+-a always,exit -F arch=b32 -S rename,unlink,unlinkat,renameat -F auid>=${UID_MIN} -F auid!=unset -F key=delete
+" >> /etc/audit/rules.d/50-delete.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + +
+ + Ensure events that modify the system's Mandatory Access Controls are collected + + +Monitor SELinux, an implementation of mandatory access controls. The parameters below monitor any write access (potential additional, deletion or modification of files in the directory) or attribute changes to the /etc/selinux/ + and /usr/share/selinux/ + directories. + + Note: + If a different Mandatory Access Control method is used, changes to the corresponding directories should be audited. + + + + + + + Network + Detect + + + + + + Applications + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +Changes to files in the /etc/selinux/ + and /usr/share/selinux/ + directories could indicate that an unauthorized user is attempting to modify access controls and change security contexts, leading to a compromise of the system. + + + + NIST SP 800-53 Rev. 5: AU-3, CM-6 + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor events that modify the system's Mandatory Access Controls. + + Example: + + +# printf "
+-w /etc/selinux -p wa -k MAC-policy
+-w /usr/share/selinux -p wa -k MAC-policy
+" >> /etc/audit/rules.d/50-MAC-policy.rules +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + +
+ + Ensure successful and unsuccessful attempts to use the chcon command are collected + + +The operating system must generate audit records for successful/unsuccessful uses of the chcon + command. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +The chcon + command is used to change file security context. Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. + Audit records can be generated from various components within the information system (e.g., module or policy filter). + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor successful and unsuccessful attempts to use the chcon + command. + + Example: + + +# {
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ [ -n "${UID_MIN}" ] && printf "
+-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=${UID_MIN} -F auid!=unset -k perm_chng
+" >> /etc/audit/rules.d/50-perm_chng.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + +
+ + Ensure successful and unsuccessful attempts to use the setfacl command are collected + + +The operating system must generate audit records for successful/unsuccessful uses of the setfacl + command + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + This utility sets Access Control Lists (ACLs) of files and directories. Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. + Audit records can be generated from various components within the information system (e.g., module or policy filter). + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor successful and unsuccessful attempts to use the setfacl + command. + + Example: + + +# {
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ [ -n "${UID_MIN}" ] && printf "
+-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=${UID_MIN} -F auid!=unset -k perm_chng
+" >> /etc/audit/rules.d/50-perm_chng.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + +
+ + Ensure successful and unsuccessful attempts to use the chacl command are collected + + +The operating system must generate audit records for successful/unsuccessful uses of the chacl + command. + + chacl + is an IRIX-compatibility command, and is maintained for those users who are familiar with its use from either XFS or IRIX. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + + chacl + changes the ACL(s) for a file or directory. Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. + Audit records can be generated from various components within the information system (e.g., module or policy filter). + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor successful and unsuccessful attempts to use the chacl + command. + + Example: + + +# {
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ [ -n "${UID_MIN}" ] && printf "
+-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=${UID_MIN} -F auid!=unset -k perm_chng
+" >> /etc/audit/rules.d/50-perm_chng.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + +
+ + Ensure successful and unsuccessful attempts to use the usermod command are collected + + +The operating system must generate audit records for successful/unsuccessful uses of the usermod + command. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +The usermod + command modifies the system account files to reflect the changes that are specified on the command line. Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. + Audit records can be generated from various components within the information system (e.g., module or policy filter). + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor successful and unsuccessful attempts to use the usermod + command. + + Example: + + +# {
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ [ -n "${UID_MIN}" ] && printf "
+-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=${UID_MIN} -F auid!=unset -k usermod
+" >> /etc/audit/rules.d/50-usermod.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + +
+ + Ensure kernel module loading unloading and modification is collected + + +Monitor the loading and unloading of kernel modules. All the loading / listing / dependency checking of modules is done by kmod + via symbolic links. + The following system calls control loading and unloading of modules: + + + init_module + - load a module + + finit_module + - load a module (used when the overhead of using cryptographically signed modules to determine the authenticity of a module can be avoided) + + delete_module + - delete a module + + create_module + - create a loadable module entry + + query_module + - query the kernel for various bits pertaining to modules + + +Any execution of the loading and unloading module programs and system calls will trigger an audit record with an identifier of modules +. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + System call structure + + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Monitoring the use of all the various ways to manipulate kernel modules could provide system administrators with evidence that an unauthorized change was made to a kernel module, possibly compromising the security of the system. + + + + NIST SP 800-53 Rev. 5: AU-3, CM-6 + + + + + Create audit rules + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor kernel module modification. + + Example: + + +#!/usr/bin/env bash
+
+{
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ [ -n "${UID_MIN}" ] && printf "
+ -a always,exit -F arch=b64 -S init_module,finit_module,delete_module,create_module,query_module -F auid>=${UID_MIN} -F auid!=unset -k kernel_modules
+ -a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=${UID_MIN} -F auid!=unset -k kernel_modules
+ " >> /etc/audit/rules.d/50-kernel_modules.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + +
+ + Ensure the audit configuration is immutable + + +Set system audit so that audit rules cannot be modified with auditctl + . Setting the flag "-e 2" forces audit to be put in immutable mode. Audit changes can only be made on system reboot. + + Note: + This setting will require the system to be rebooted to update the active auditd + configuration settings. + + + + + + + Data + Protect + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + In immutable mode, unauthorized users cannot execute changes to the audit system to potentially hide malicious activity and then put the audit rules back. Users would most likely notice a system reboot and that could alert administrators of an attempt to make unauthorized audit changes. + + + + + + NIST SP 800-53 Rev. 5: AC-3, AU-3, AU-3(1), MP-2 + + + + +Edit or create the file /etc/audit/rules.d/99-finalize.rules + and add the line -e 2 + at the end of the file: + + Example: + + # printf '\n%s' "-e 2" >> /etc/audit/rules.d/99-finalize.rules + + + Load audit rules + + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + + + + + + + + + + + + + Ensure the running and on disk configuration is the same + + The Audit system have both on disk and running configuration. It is possible for these configuration settings to differ. + + Note: + Due to the limitations of augenrules + and auditctl +, it is not absolutely guaranteed that loading the rule sets via augenrules --load + will result in all rules being loaded or even that the user will be informed if there was a problem loading the rules. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + + Potential reboot required + + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + + + Configuration differences between what is currently running and what is on disk could cause unexpected problems or may give a false impression of compliance requirements. + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + If the rules are not aligned across all three () areas, run the following command to merge and load all rules: + # augenrules --load + + Check if reboot is required. + if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then echo "Reboot required to load rules"; fi + + + + + +
+ + Configure auditd File Access + + Without the capability to restrict which roles and individuals can select which events are audited, unauthorized personnel may be able to prevent the auditing of critical events. + + + Ensure the audit log file directory mode is configured + + The audit log directory contains audit log files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Audit information includes all information including: audit records, audit settings and audit reports. This information is needed to successfully audit system activity. This information must be protected from unauthorized modification or deletion. If this information were to be compromised, forensic analysis and discovery of the true source of potentially malicious system activity is impossible to achieve. + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + Run the following command to configure the audit log directory to have a mode of "0750" or less permissive: + # chmod g-w,o-rwx "$(dirname "$(awk -F= '/^\s*log_file\s*/{print $2}' /etc/audit/auditd.conf | xargs)")" + + + + + + + + + + + + + + Ensure audit log files mode is configured + + Audit log files contain information about the system and system activity. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to audit records can reveal system and configuration data to attackers, potentially compromising its confidentiality. + + + + + + + +Run the following command to remove more permissive mode than 0640 + from audit log files: + # [ -f /etc/audit/auditd.conf ] && find "$(dirname $(awk -F "=" '/^\s*log_file/ {print $2}' /etc/audit/auditd.conf | xargs))" -type f -perm /0137 -exec chmod u-x,g-wx,o-rwx {} + + + + + + + + + + + + + + + Ensure audit log files owner is configured + + Audit log files contain information about the system and system activity. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to audit records can reveal system and configuration data to attackers, potentially compromising its confidentiality. + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + +Run the following command to configure the audit log files to be owned by the root + user: + # [ -f /etc/audit/auditd.conf ] && find "$(dirname $(awk -F "=" '/^\s*log_file/ {print $2}' /etc/audit/auditd.conf | xargs))" -type f ! -user root -exec chown root {} + + + + + + + + + + + + + + + Ensure audit log files group owner is configured + + Audit log files contain information about the system and system activity. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to audit records can reveal system and configuration data to attackers, potentially compromising its confidentiality. + + + + + + + +Run the following command to configure the audit log files to be owned by adm + group: + # find $(dirname $(awk -F"=" '/^\s*log_file\s*=\s*/ {print $2}' /etc/audit/auditd.conf | xargs)) -type f \( ! -group adm -a ! -group root \) -exec chgrp adm {} + + + +Run the following command to configure the audit log files to be owned by the adm + group: + # chgrp adm /var/log/audit/ + + +Run the following command to set the log_group + parameter in the audit configuration file to log_group = adm +: + # sed -ri 's/^\s*#?\s*log_group\s*=\s*\S+(\s*#.*)?.*$/log_group = adm\1/' /etc/audit/auditd.conf + + Run the following command to restart the audit daemon to reload the configuration file: + # systemctl restart auditd + + + + + + + + + + + + + + Ensure audit configuration files mode is configured + + Audit configuration files control auditd and what events are audited. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to the audit configuration files could allow unauthorized personnel to prevent the auditing of critical events. + Misconfigured audit configuration files may prevent the auditing of critical events or impact the system's performance by overwhelming the audit log. Misconfiguration of the audit configuration files may also make it more difficult to establish and investigate events relating to an incident. + + + + + + + Run the following command to remove more permissive mode than 0640 from the audit configuration files: + # find /etc/audit/ -type f \( -name '*.conf' -o -name '*.rules' \) -exec chmod u-x,g-wx,o-rwx {} + + + + + + + + + + + + + + + + + + + + Ensure audit configuration files owner is configured + + Audit configuration files control auditd and what events are audited. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to the audit configuration files could allow unauthorized personnel to prevent the auditing of critical events. + Misconfigured audit configuration files may prevent the auditing of critical events or impact the system's performance by overwhelming the audit log. Misconfiguration of the audit configuration files may also make it more difficult to establish and investigate events relating to an incident. + + + + + + + +Run the following command to change ownership to root + user: + # find /etc/audit/ -type f \( -name '*.conf' -o -name '*.rules' \) ! -user root -exec chown root {} + + + + + + + + + + + + + + + + + + + + Ensure audit configuration files group owner is configured + + Audit configuration files control auditd and what events are audited. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to the audit configuration files could allow unauthorized personnel to prevent the auditing of critical events. + Misconfigured audit configuration files may prevent the auditing of critical events or impact the system's performance by overwhelming the audit log. Misconfiguration of the audit configuration files may also make it more difficult to establish and investigate events relating to an incident. + + + + + + + +Run the following command to change group to root +: + # find /etc/audit/ -type f \( -name '*.conf' -o -name '*.rules' \) ! -group root -exec chgrp root {} + + + + + + + + + + + + + + + + + + + + Ensure audit tools mode is configured + + Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Protecting audit information includes identifying and protecting the tools used to view and manipulate log data. Protecting audit tools is necessary to prevent unauthorized operation on audit information. + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + Run the following command to remove more permissive mode from the audit tools: + # chmod go-w /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/augenrules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure audit tools owner is configured + + Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Protecting audit information includes identifying and protecting the tools used to view and manipulate log data. Protecting audit tools is necessary to prevent unauthorized operation on audit information. + + + + + + + +Run the following command to change the owner of the audit tools to the root + user: + # chown root /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/augenrules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure audit tools group owner is configured + + Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Protecting audit information includes identifying and protecting the tools used to view and manipulate log data. Protecting audit tools is necessary to prevent unauthorized operation on audit information. + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + +Run the following command to change group ownership to the groop root +: + # chgrp root /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/augenrules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + System Maintenance + + Recommendations in this section are intended as maintenance and are intended to be checked on a frequent basis to ensure system stability. Many recommendations do not have quick remediations and require investigation into the cause and best fix available and may indicate an attempted breach of system security. + + + System File Permissions + + This section provides guidance on securing aspects of system files and directories. + + + Ensure permissions on /etc/passwd are configured + + +The /etc/passwd + file contains user account information that is used by many system utilities and therefore must be readable for these utilities to operate. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that the /etc/passwd + file is protected from unauthorized write access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/passwd +: + +# chmod u-x,go-wx /etc/passwd
+# chown root:root /etc/passwd +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/passwd- are configured + + +The /etc/passwd- + file contains backup user account information. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that the /etc/passwd- + file is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/passwd- +: + +# chmod u-x,go-wx /etc/passwd-
+# chown root:root /etc/passwd- +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/group are configured + + +The /etc/group + file contains a list of all the valid groups defined in the system. The command below allows read/write access for root and read access for everyone else. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +The /etc/group + file needs to be protected from unauthorized changes by non-privileged users, but needs to be readable as this information is used with many non-privileged programs. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/group +: + +# chmod u-x,go-wx /etc/group
+# chown root:root /etc/group +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/group- are configured + + +The /etc/group- + file contains a backup list of all the valid groups defined in the system. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that the /etc/group- + file is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/group- +: + +# chmod u-x,go-wx /etc/group-
+# chown root:root /etc/group- +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/shadow are configured + + +The /etc/shadow + file is used to store the information about user accounts that is critical to the security of those accounts, such as the hashed password and other security information. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +If attackers can gain read access to the /etc/shadow + file, they can easily run a password cracking program against the hashed password to break it. Other security information that is stored in the /etc/shadow + file (such as expiration) could also be useful to subvert the user accounts. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/shadow +: + +# chown root:root /etc/shadow
+# chmod 0000 /etc/shadow +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/shadow- are configured + + +The /etc/shadow- + file is used to store backup information about user accounts that is critical to the security of those accounts, such as the hashed password and other security information. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that the /etc/shadow- + file is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/shadow- +: + +# chown root:root /etc/shadow-
+# chmod 0000 /etc/shadow- +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/gshadow are configured + + +The /etc/gshadow + file is used to store the information about groups that is critical to the security of those accounts, such as the hashed password and other security information. + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + +If attackers can gain read access to the /etc/gshadow + file, they can easily run a password cracking program against the hashed password to break it. Other security information that is stored in the /etc/gshadow + file (such as group administrators) could also be useful to subvert the group. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/gshadow +: + +# chown root:root /etc/gshadow
+# chmod 0000 /etc/gshadow +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/gshadow- are configured + + +The /etc/gshadow- + file is used to store backup information about groups that is critical to the security of those accounts, such as the hashed password and other security information. + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + +It is critical to ensure that the /etc/gshadow- + file is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/gshadow- +: + +# chown root:root /etc/gshadow-
+# chmod 0000 /etc/gshadow- +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/shells are configured + + + /etc/shells + is a text file which contains the full pathnames of valid login shells. This file is consulted by chsh + and available to be queried by other programs. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that the /etc/shells + file is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/shells +: + +# chmod u-x,go-wx /etc/shells
+# chown root:root /etc/shells +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/security/opasswd are configured + + + /etc/security/opasswd + and it's backup /etc/security/opasswd.old + hold user's previous passwords if pam_unix + or pam_pwhistory + is in use on the system + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that /etc/security/opasswd + is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/security/opasswd + and /etc/security/opasswd.old + is they exist: + +# [ -e "/etc/security/opasswd" ] && chmod u-x,go-rwx /etc/security/opasswd
+# [ -e "/etc/security/opasswd" ] && chown root:root /etc/security/opasswd
+# [ -e "/etc/security/opasswd.old" ] && chmod u-x,go-rwx /etc/security/opasswd.old
+# [ -e "/etc/security/opasswd.old" ] && chown root:root /etc/security/opasswd.old +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure world writable files and directories are secured + + +World writable files are the least secure. Data in world-writable files can be modified and compromised by any user on the system. World writable files may also indicate an incorrectly written script or program that could potentially be the cause of a larger compromise to the system's integrity. See the chmod(2) + man page for more information. + Setting the sticky bit on world writable directories prevents users from deleting or renaming files in that directory that are not owned by them. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Data in world-writable files can be modified and compromised by any user on the system. World writable files may also indicate an incorrectly written script or program that could potentially be the cause of a larger compromise to the system's integrity. + +This feature prevents the ability to delete or rename files in world writable directories (such as /tmp + ) that are owned by another user. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + +World Writable Files: + + +It is recommended that write access is removed from other + with the command ( chmod o-w <filename> + ), but always consult relevant vendor documentation to avoid breaking any application dependencies on a given file. + + + +World Writable Directories: + + +Set the sticky bit on all world writable directories with the command ( chmod a+t <directory_name> + ) + + + + Run the following script to: + + Remove other write permission from any world writable files + Add the sticky bit to all world writable directories + + +#!/usr/bin/env bash
+
+{
+ l_smask='01000'
+ a_file=(); a_dir=() # Initialize arrays
+ a_path=(! -path "/run/user/*" -a ! -path "/proc/*" -a ! -path "*/containerd/*" -a ! -path "*/kubelet/pods/*" -a ! -path "*/kubelet/plugins/*" -a ! -path "/sys/*" -a ! -path "/snap/*")
+ while IFS= read -r l_mount; do
+ while IFS= read -r -d $'\0' l_file; do
+ if [ -e "$l_file" ]; then
+ l_mode="$(stat -Lc '%#a' "$l_file")"
+ if [ -f "$l_file" ]; then # Remove excess permissions from WW files
+ echo -e " - File: \"$l_file\" is mode: \"$l_mode\"\n - removing write permission on \"$l_file\" from \"other\""
+ chmod o-w "$l_file"
+ fi
+ if [ -d "$l_file" ]; then # Add sticky bit
+ if [ ! $(( $l_mode & $l_smask )) -gt 0 ]; then
+ echo -e " - Directory: \"$l_file\" is mode: \"$l_mode\" and doesn't have the sticky bit set\n - Adding the sticky bit"
+ chmod a+t "$l_file"
+ fi
+ fi
+ fi
+ done < <(find "$l_mount" -xdev \( "${a_path[@]}" \) \( -type f -o -type d \) -perm -0002 -print0 2> /dev/null)
+ done < <(findmnt -Dkerno fstype,target | awk '($1 !~ /^\s*(nfs|proc|smb|vfat|iso9660|efivarfs|selinuxfs)/ && $2 !~ /^(\/run\/user\/|\/tmp|\/var\/tmp)/){print $2}')
+} +
+
+
+
+ + + + + + +
+ + Ensure no files or directories without an owner and a group exist + + Administrators may delete users or groups from the system and neglect to remove all files and/or directories owned by those users or groups. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + A new user or group who is assigned a deleted user's user ID or group ID may then end up "owning" a deleted user or group's files, and thus have more access on the system than was intended. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + Remove or set ownership and group ownership of these files and/or directories to an active user on the system as appropriate. + + + + + + + + + + + + Ensure SUID and SGID files are reviewed + + The owner of a file can set the file's permissions to run with the owner's or group's permissions, even if the user running the program is not the owner or a member of the group. The most common reason for a SUID or SGID program is to enable users to perform functions (such as changing their password) that require root privileges. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + There are valid reasons for SUID and SGID programs, but it is important to identify and review such programs to ensure they are legitimate. Review the files returned by the action in the audit section and check to see if system binaries have a different checksum than what from the package. This is an indication that the binary may have been replaced. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5, AC-3, MP-2 + + + + Ensure that no rogue SUID or SGID programs have been introduced into the system. Review the files returned by the action in the Audit section and confirm the integrity of these binaries. + + + + +
+ + Local User and Group Settings + + This section provides guidance on securing aspects of the local users and groups. + + Note: + The recommendations in this section check local users and groups. Any users or groups from other sources such as LDAP will not be audited. In a domain environment similar checks should be performed against domain users and groups. + + + Ensure accounts in /etc/passwd use shadowed passwords + + +Local accounts can uses shadowed passwords. With shadowed passwords, The passwords are saved in shadow password file, /etc/shadow +, encrypted by a salted one-way hash. Accounts with a shadowed password have an x + in the second field in /etc/passwd +. + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + +The pwconv + command creates shadow from passwd + and an optionally existing shadow +. + + +The pwunconv + command creates passwd + from passwd + and shadow + and then removes shadow +. + +The grpconv + command creates gshadow + from group + and an optionally existing gshadow +. + +The grpunconv + command creates group + from group + and gshadow + and then removes gshadow +. + + +These four programs all operate on the normal and shadow password and group files: /etc/passwd +, /etc/group +, /etc/shadow +, and /etc/gshadow +. + +Each program acquires the necessary locks before conversion. pwconv + and grpconv + are similar. First, entries in the shadowed file which don't exist in the main file are removed. Then, shadowed entries which don't have x' as the password in the main file are updated. Any missing shadowed entries are added. Finally, passwords in the main file are replaced with +x'. These programs can be used for initial conversion as well to update the shadowed file if the main file is edited by hand. + + pwconv + will use the values of PASS_MIN_DAYS +, PASS_MAX_DAYS +, and PASS_WARN_AGE + from /etc/login.defs when adding new entries to /etc/shadow. + + pwunconv + and grpunconv + are similar. Passwords in the main file are updated from the shadowed file. Entries which exist in the main file but not in the shadowed file are left alone. Finally, the shadowed file is removed. Some password aging information is lost by pwunconv +. It will convert what it can. + + + + +The /etc/passwd + file also contains information like user ID's and group ID's that are used by many system programs. Therefore, the /etc/passwd + file must remain world readable. In spite of encoding the password with a randomly-generated one-way hash function, an attacker could still break the system if they got access to the /etc/passwd + file. This can be mitigated by using shadowed passwords, thus moving the passwords in the /etc/passwd + file to /etc/shadow +. The /etc/shadow + file is set so only root will be able to read and write. This helps mitigate the risk of an attacker gaining access to the encoded passwords with which to perform a dictionary attack. + + Note: + + + All accounts must have passwords or be locked to prevent the account from being used by an unauthorized user. + +A user account with an empty second field in /etc/passwd + allows the account to be logged into by providing only the username. + + + + + NIST SP 800-53 Rev. 5: IA-5 + PWCONV(8) + + + + +Run the following command to set accounts to use shadowed passwords and migrate passwords in /etc/passwd + to /etc/shadow +: + # pwconv + + Investigate to determine if the account is logged in and what it is being used for, to determine if it needs to be forced off. + + + + + + + + + + + Ensure /etc/shadow password fields are not empty + + An account with an empty password field means that anybody may log in as that user without providing a password. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + All accounts must have passwords or be locked to prevent the account from being used by an unauthorized user. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +If any accounts in the /etc/shadow + file do not have a password, run the following command to lock the account until it can be determined why it does not have a password: + # passwd -l <username> + + Also, check to see if the account is logged in and investigate what it is being used for to determine if it needs to be forced off. + + + + + + + + + + + + Ensure all groups in /etc/passwd exist in /etc/group + + +Over time, system administration errors and changes can lead to groups being defined in /etc/passwd + but not in /etc/group + . + + + + + + + Data + Protect + + + + + Protect + + + + + + +Groups defined in the /etc/passwd + file but not in the /etc/group + file pose a threat to system security since group permissions are not properly managed. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Analyze the output of the Audit step above and perform the appropriate action to correct any discrepancies found. + + + + + + + + + + + + Ensure no duplicate UIDs exist + + +Although the useradd + program will not let you create a duplicate User ID (UID), it is possible for an administrator to manually edit the /etc/passwd + file and change the UID field. + + + Users must be assigned unique UIDs for accountability and to ensure appropriate access protections. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Based on the results of the audit script, establish unique UIDs and review all files owned by the shared UIDs to determine which UID they are supposed to belong to. + + + + + + + + + + + + Ensure no duplicate GIDs exist + + +Although the groupadd + program will not let you create a duplicate Group ID (GID), it is possible for an administrator to manually edit the /etc/group + file and change the GID field. + + + + +You can also use the grpck + command to check for other inconsistencies in the /etc/group + file. + + + + User groups must be assigned unique GIDs for accountability and to ensure appropriate access protections. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Based on the results of the audit script, establish unique GIDs and review all files owned by the shared GID to determine which group they are supposed to belong to. + + + + + + + + + + + + Ensure no duplicate user names exist + + +Although the useradd + program will not let you create a duplicate user name, it is possible for an administrator to manually edit the /etc/passwd + file and change the user name. + + + +If a user is assigned a duplicate user name, it will create and have access to files with the first UID for that username in /etc/passwd + . For example, if "test4" has a UID of 1000 and a subsequent "test4" entry has a UID of 2000, logging in as "test4" will use UID 1000. Effectively, the UID is shared, which is a security problem. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Based on the results of the audit script, establish unique user names for the users. File ownerships will automatically reflect the change as long as the users have unique UIDs. + + + + + + + + + + + + Ensure no duplicate group names exist + + +Although the groupadd + program will not let you create a duplicate group name, it is possible for an administrator to manually edit the /etc/group + file and change the group name. + + + +If a group is assigned a duplicate group name, it will create and have access to files with the first GID for that group in /etc/group + . Effectively, the GID is shared, which is a security problem. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Based on the results of the audit script, establish unique names for the user groups. File group ownerships will automatically reflect the change as long as the groups have unique GIDs. + + + + + + + + + + + + Ensure local interactive user home directories are configured + + +The user home directory is space defined for the particular user to set local environment variables and to store personal files. While the system administrator can establish secure permissions for users' home directories, the users can easily override these. Users can be defined in /etc/passwd + without a home directory or with a home directory that does not actually exist. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Since the user is accountable for files stored in the user home directory, the user must be the owner of the directory. Group or world-writable user home directories may enable malicious users to steal or modify other users' data or to gain another user's system privileges. If the user's home directory does not exist or is unassigned, the user will be placed in "/" and will not be able to write any files or have local environment variables set. + + + + + + + If a local interactive users' home directory is undefined and/or doesn't exist, follow local site policy and perform one of the following: + + Lock the user account + Remove the user from the system + +create a directory for the user. If undefined, edit /etc/passwd + and add the absolute path to the directory to the last field of the user. + + Run the following script to: + + Remove excessive permissions from local interactive users home directories + Update the home directory's owner + + +#!/usr/bin/env bash
+
+{
+ l_output2=""
+ l_valid_shells="^($( awk -F\/ '$NF != "nologin" {print}' /etc/shells | sed -rn '/^\//{s,/,\\\\/,g;p}' | paste -s -d '|' - ))$"
+ unset a_uarr && a_uarr=() # Clear and initialize array
+ while read -r l_epu l_eph; do # Populate array with users and user home location
+ a_uarr+=("$l_epu $l_eph")
+ done <<< "$(awk -v pat="$l_valid_shells" -F: '$(NF) ~ pat { print $1 " " $(NF-1) }' /etc/passwd)"
+ l_asize="${#a_uarr[@]}" # Here if we want to look at number of users before proceeding
+ [ "$l_asize " -gt "10000" ] && echo -e "\n ** INFO **\n - \"$l_asize\" Local interactive users found on the system\n - This may be a long running process\n"
+ while read -r l_user l_home; do
+ if [ -d "$l_home" ]; then
+ l_mask='0027'
+ l_max="$( printf '%o' $(( 0777 & ~$l_mask)) )"
+ while read -r l_own l_mode; do
+ if [ "$l_user" != "$l_own" ]; then
+ l_output2="$l_output2\n - User: \"$l_user\" Home \"$l_home\" is owned by: \"$l_own\"\n - changing ownership to: \"$l_user\"\n"
+ chown "$l_user" "$l_home"
+ fi
+ if [ $(( $l_mode & $l_mask )) -gt 0 ]; then
+ l_output2="$l_output2\n - User: \"$l_user\" Home \"$l_home\" is mode: \"$l_mode\" should be mode: \"$l_max\" or more restrictive\n - removing excess permissions\n"
+ chmod g-w,o-rwx "$l_home"
+ fi
+ done <<< "$(stat -Lc '%U %#a' "$l_home")"
+ else
+ l_output2="$l_output2\n - User: \"$l_user\" Home \"$l_home\" Doesn't exist\n - Please create a home in accordance with local site policy"
+ fi
+ done <<< "$(printf '%s\n' "${a_uarr[@]}")"
+ if [ -z "$l_output2" ]; then # If l_output2 is empty, we pass
+ echo -e " - No modification needed to local interactive users home directories"
+ else
+ echo -e "\n$l_output2"
+ fi
+} +
+
+
+
+ + + + + + +
+ + Ensure local interactive user dot files access is configured + + While the system administrator can establish secure permissions for users' "dot" files, the users can easily override these. + + + .forward + file specifies an email address to forward the user's mail to. + + .rhost + file provides the "remote authentication" database for the rcp, rlogin, and rsh commands and the rcmd() function. These files bypass the standard password-based user authentication mechanism. They specify remote hosts and users that are considered trusted (i.e. are allowed to access the local system without supplying a password) + + .netrc + file contains data for logging into a remote host or passing authentication to an API. + + .bash_history + file keeps track of the user’s commands. + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + User configuration files with excessive or incorrect access may enable malicious users to steal or modify other users' data or to gain another user's system privileges. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Making global modifications to users' files without alerting the user community can result in unexpected outages and unhappy users. Therefore, it is recommended that a monitoring policy be established to report user dot file permissions and determine the action to be taken in accordance with site policy. + The following script will: + + +remove excessive permissions on dot + files within interactive users' home directories + +change ownership of dot + files within interactive users' home directories to the user + +change group ownership of dot + files within interactive users' home directories to the user's primary group + +list .forward + and .rhost + files to be investigated and manually deleted + + +#!/usr/bin/env bash
+
+{
+ a_output2=(); a_output3=()
+ l_maxsize="1000" # Maximum number of local interactive users before warning (Default 1,000)
+ l_valid_shells="^($( awk -F\/ '$NF != "nologin" {print}' /etc/shells | sed -rn '/^\//{s,/,\\\\/,g;p}' | paste -s -d '|' - ))$"
+ a_user_and_home=() # Create array with local users and their home directories
+ while read -r l_local_user l_local_user_home; do # Populate array with users and user home location
+ [[ -n "$l_local_user" && -n "$l_local_user_home" ]] && a_user_and_home+=("$l_local_user:$l_local_user_home")
+ done <<< "$(awk -v pat="$l_valid_shells" -F: '$(NF) ~ pat { print $1 " " $(NF-1) }' /etc/passwd)"
+ l_asize="${#a_user_and_home[@]}" # Here if we want to look at number of users before proceeding
+ [ "${#a_user_and_home[@]}" -gt "$l_maxsize" ] && printf '%s\n' "" " ** INFO **" \
+ " - \"$l_asize\" Local interactive users found on the system" \
+ " - This may be a long running check" ""
+ file_access_fix()
+ {
+ a_access_out=()
+ l_max="$( printf '%o' $(( 0777 & ~$l_mask)) )"
+ if [ $(( $l_mode & $l_mask )) -gt 0 ]; then
+ printf '%s\n' "" " - File: \"$l_hdfile\" is mode: \"$l_mode\" and should be mode: \"$l_max\" or more restrictive" \
+ " Updating file: \"$l_hdfile\" to be mode: \"$l_max\" or more restrictive"
+ chmod "$l_change" "$l_hdfile"
+ fi
+ if [[ ! "$l_owner" =~ ($l_user) ]]; then
+ printf '%s\n' "" " - File: \"$l_hdfile\" owned by: \"$l_owner\" and should be owned by \"${l_user//|/ or }\"" \
+ " Updating file: \"$l_hdfile\" to be owned by \"${l_user//|/ or }\""
+ chown "$l_user" "$l_hdfile"
+ fi
+ if [[ ! "$l_gowner" =~ ($l_group) ]]; then
+ printf '%s\n' "" " - File: \"$l_hdfile\" group owned by: \"$l_gowner\" and should be group owned by \"${l_group//|/ or }\"" \
+ " Updating file: \"$l_hdfile\" to be group owned by \"${l_group//|/ or }\""
+ chgrp "$l_group" "$l_hdfile"
+ fi
+ }
+ while IFS=: read -r l_user l_home; do
+ a_dot_file=(); a_netrc=(); a_netrc_warn=(); a_bhout=(); a_hdirout=()
+ if [ -d "$l_home" ]; then
+ l_group="$(id -gn "$l_user" | xargs)";l_group="${l_group// /|}"
+ while IFS= read -r -d $'\0' l_hdfile; do
+ while read -r l_mode l_owner l_gowner; do
+ case "$(basename "$l_hdfile")" in
+ .forward | .rhost )
+ a_dot_file+=(" - File: \"$l_hdfile\" exists" " Please review and manually delete this file") ;;
+ .netrc )
+ l_mask='0177'; l_change="u-x,go-rwx"; file_access_fix
+ a_netrc_warn+=(" - File: \"$l_hdfile\" exists") ;;
+ .bash_history )
+ l_mask='0177'; l_change="u-x,go-rwx"; file_access_fix ;;
+ * )
+ l_mask='0133'; l_change="u-x,go-wx"; file_access_fix ;;
+ esac
+ done < <(stat -Lc '%#a %U %G' "$l_hdfile")
+ done < <(find "$l_home" -xdev -type f -name '.*' -print0)
+ fi
+ [ "${#a_dot_file[@]}" -gt 0 ] && a_output2+=(" - User: \"$l_user\" Home Directory: \"$l_home\"" "${a_dot_file[@]}")
+ [ "${#a_netrc_warn[@]}" -gt 0 ] && a_output3+=(" - User: \"$l_user\" Home Directory: \"$l_home\"" "${a_netrc_warn[@]}")
+ done <<< "$(printf '%s\n' "${a_user_and_home[@]}")"
+ [ "${#a_output3[@]}" -gt 0 ] && printf '%s\n' "" " ** WARNING **" "${a_output3[@]}" ""
+ [ "${#a_output2[@]}" -gt 0 ] && printf '%s\n' "" "${a_output2[@]}"
+} +
+
+
+
+ + + + + + +
+
+
+ + + + + + + + + + +Ol1MzJU3Q9rKswRJLjQ7qdJgKKYhqeqlvCQ1NbeH3hw= + + + +yI5XYzHOFbuQLkpppMH3gTDFHYcM8Dn7E5fTcqr5CVFTGyTXkCz2pVLzM8bLUzz+GfzA6Yd1uLDi +5zAWcJln2boGtMAUDXG3YiIuGDDiht4MXV3Pdcz1YbnYDcN4HDw0aPf7RA2cvu4ZKSLt4Uq6iWVK +Gd2zAgfq+qVxIyk5khmIt2RTnkGu2V9xZui9qoU2/yXH2sbDdd4cQvGGHNiJx/RYI6HyYBUXTpqm +e3e2gNzHsGGZQ+GYAO2DgC5r6cZQyM2OqRj3EzjsNaEkKoP0+LBREBc1FPiN9cZgCAE9rOPFeVvZ +TWpF2gc+Af4o3imjlxNhjiLdZJMpDMdQRzC9FVroJJD4DIIJeWJyK7O2gwUAl5OOsVeHJkJN5epd +/98hEDFdDGDiFW/zEZD0gYj4S+H15ZbCKWo7ayyzOu9+yDm1YAzJVBYww1B0uEDEsBts5+KdOEyR +MKhoVSgYEQuZlBeHjtEUF/pqBkIH3eqD32jh9YpkjZ5oirbIG/Ofne3m + + + + +MIIHbDCCBVSgAwIBAgIQCdceADCnzKkglkQGavVKYzANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQG +EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQg +RzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMB4XDTIzMTAxODAwMDAwMFoX +DTI2MTAxODIzNTk1OVowgfQxEzARBgsrBgEEAYI3PAIBAxMCVVMxGTAXBgsrBgEEAYI3PAIBAhMI +TWFyeWxhbmQxHTAbBgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRIwEAYDVQQFEwlEMDYwNTg4 +MTIxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhOZXcgWW9yazEXMBUGA1UEBxMORWFzdCBHcmVlbmJ1 +c2gxKjAoBgNVBAoTIUNlbnRlciBmb3IgSW50ZXJuZXQgU2VjdXJpdHksIEluYzEqMCgGA1UEAxMh +Q2VudGVyIGZvciBJbnRlcm5ldCBTZWN1cml0eSwgSW5jMIIBojANBgkqhkiG9w0BAQEFAAOCAY8A +MIIBigKCAYEAyZwTJr4Kb7QEDnVY3BEkPoS3fn+XoxhCTfFvlMk8zXDcR4pFMgPkXTrZ1KbENUIV +skIhy1vmnh61vwSL0lcdzesCekKSxLqrEA4xunZk1MB5mLbHXchSSpI1co1vaSzJjkTYP1UsUVQz +NoOzV0LNCoZdahjGTHSduWvbrHTEeD6/jvUj3AVWrTx6krzgYA3ozAxtUnapK7IrZERuGOSwTrgR +Lr1aieQdFy8haW1YL+ks5HmLRvcoGp+J68GX/zfMTduvWxhcpXm4txKc4iJqGCRmyBU2XjGjpDBX +ndxHgT4edRLz5PwWSeZUh8/tWOGFwnVw7njoOa0sFixy96H2BqEn+yPeRALyf59rjtPL66tsXuaA +ilB63yqbH42mnkxHTX5zcEm337GzTOGRccsYzN/ApVuXDeMfsO+sSUosrimnxm9QTOyKcex5h1As +rxBVfTw+Zf7jP1YRlmMlGPG1zffK2KjJhD6ivFi2sIDThEQLxIAC8XOjp2ZXvhx87yNTAgMBAAGj +ggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNVHQ4EFgQUX2/J9FT5 +5BNF7JqfAl9SJkv3pf0wPQYDVR0gBDYwNDAyBgVngQwBAzApMCcGCCsGAQUFBwIBFhtodHRwOi8v +d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMD +MIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy +dXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8v +Y3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEz +ODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3Nw +LmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp +Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0T +BAIwADANBgkqhkiG9w0BAQsFAAOCAgEASLOHf1FQ7TrGqEoVXYoeNSkRWdVCUGd1JCDs7Hb9sLd3 +eQuipZ4EorQS/9qMiVMhmlJfF75sNVQhr1K5UtjrDRFMTzYmh78hAyovowzgyVKdGFqiVVr5m+mb +vVyglLlA4V076LMBaKRknm1Dt8r0/5GSRCrkP2origpYMMaocN+iBX9+MImnh3J1Ehn0nRUhW86Q +mt3/YCVIaQZisv+KmOxyAq1m6fiIYIvXY+cH2dDIheoVteb/NjYnKVqE6xTpOsxF16pykXiM9yk3 +Q9nlJS36KEGRP4wrySU4eIBnTcv4mimZjkWj6bIvk8otNJ14FbyltaPwIc3dE3oPGfSmCrkxs73j +iqBs2TXLvblEmOhY8ko3xVdTm0zMP50MQVO948fz9yF+xdnPdMPT1/lCmGp6iWYRzxnvEkA1+HFl +yy9YztrM8WNAhXirpUZg3HWQE2ONLKMtqQrkIHD2nikda77flk6oirqDuCf8Q7g8s86/KabTdxrw +pRmsRjDkAfKVae58ctaKbmF32oU9BGk+9MPG8HF5Yfoh12DE1fLZcPzZrROrDFJGGxwhv5FlnxGg +6G7jKi68erZnpaOPayK0bXyZNunqkI9IbIuPqMw5qaCZZgN6AEHIbGTKdrWEf11DmR7MdTjv3/vu +JWtIXgk3kQFX27w9JhqV6QogVmzIfEQ= + + + + +yZwTJr4Kb7QEDnVY3BEkPoS3fn+XoxhCTfFvlMk8zXDcR4pFMgPkXTrZ1KbENUIVskIhy1vmnh61 +vwSL0lcdzesCekKSxLqrEA4xunZk1MB5mLbHXchSSpI1co1vaSzJjkTYP1UsUVQzNoOzV0LNCoZd +ahjGTHSduWvbrHTEeD6/jvUj3AVWrTx6krzgYA3ozAxtUnapK7IrZERuGOSwTrgRLr1aieQdFy8h +aW1YL+ks5HmLRvcoGp+J68GX/zfMTduvWxhcpXm4txKc4iJqGCRmyBU2XjGjpDBXndxHgT4edRLz +5PwWSeZUh8/tWOGFwnVw7njoOa0sFixy96H2BqEn+yPeRALyf59rjtPL66tsXuaAilB63yqbH42m +nkxHTX5zcEm337GzTOGRccsYzN/ApVuXDeMfsO+sSUosrimnxm9QTOyKcex5h1AsrxBVfTw+Zf7j +P1YRlmMlGPG1zffK2KjJhD6ivFi2sIDThEQLxIAC8XOjp2ZXvhx87yNT +AQAB + + + +
\ No newline at end of file diff --git a/test/sample_data/xccdf/cis/CIS_Amazon_Linux_2_Benchmark_v3.0.0-xccdf.xml b/test/sample_data/xccdf/cis/CIS_Amazon_Linux_2_Benchmark_v3.0.0-xccdf.xml new file mode 100644 index 000000000..5f408f78d --- /dev/null +++ b/test/sample_data/xccdf/cis/CIS_Amazon_Linux_2_Benchmark_v3.0.0-xccdf.xml @@ -0,0 +1,26339 @@ + + accepted + CIS Amazon Linux 2 Benchmark + + This document provides prescriptive guidance for establishing a secure configuration posture for Amazon Linux 2 running on x86_64 platforms. + +The guidance within broadly assumes that operations are being performed as the root + user, and executed under the default Bash version for the applicable distribution. Operations performed using sudo + instead of the root + user, or executed under another shell, may produce unexpected results, or fail to make the intended changes to the system. Non-root users may not be able to access certain areas of the system, especially after remediation has been performed. It is advisable to verify root + users path integrity and the integrity of any programs being run prior to execution of commands and scripts included in this benchmark. + +The default prompt for the root + user is # +, and as such all sample commands will have # + as an additional indication that it is to be executed as root +. + +To obtain the latest version of this guide, please visit http://workbench.cisecurity.org +. If you have questions, comments, or have identified ways to improve this guide, please write us at feedback@cisecurity.org +. + + BACKGROUND. + The Center for Internet Security ("CIS") provides benchmarks, scoring tools, software, data, information, suggestions, ideas, and other services and materials from the CIS website or elsewhere ("Products") as a public service to Internet users worldwide. Recommendations contained in the Products ("Recommendations") result from a consensus-building process that involves many security experts and are generally generic in nature. The Recommendations are intended to provide helpful information to organizations attempting to evaluate or improve the security of their networks, systems, and devices. Proper use of the Recommendations requires careful analysis and adaptation to specific user requirements. The Recommendations are not in any way intended to be a "quick fix" for anyone's information security needs. + NO REPRESENTATIONS, WARRANTIES, OR COVENANTS. + CIS makes no representations, warranties, or covenants whatsoever as to (i) the positive or negative effect of the Products or the Recommendations on the operation or the security of any particular network, computer system, network device, software, hardware, or any component of any of the foregoing or (ii) the accuracy, reliability, timeliness, or completeness of the Products or the Recommendations. CIS is providing the Products and the Recommendations "as is" and "as available" without representations, warranties, or covenants of any kind. USER AGREEMENTS. + By using the Products and/or the Recommendations, I and/or my organization ("We") agree and acknowledge that: + 1. No network, system, device, hardware, software, or component can be made fully secure; + 2. We are using the Products and the Recommendations solely at our own risk; + 3. We are not compensating CIS to assume any liabilities associated with our use of the Products or the Recommendations, even risks that result from CIS's negligence or failure to perform; + 4. We have the sole responsibility to evaluate the risks and benefits of the Products and Recommendations to us and to adapt the Products and the Recommendations to our particular circumstances and requirements; + 5. Neither CIS, nor any CIS Party (defined below) has any responsibility to make any corrections, updates, upgrades, or bug fixes; or to notify us of the need for any such corrections, updates, upgrades, or bug fixes; and + 6. Neither CIS nor any CIS Party has or will have any liability to us whatsoever (whether based in contract, tort, strict liability or otherwise) for any direct, indirect, incidental, consequential, or special damages (including without limitation loss of profits, loss of sales, loss of or damage to reputation,loss of customers, loss of software, data, information or emails, loss of privacy, loss of use of any computer or other equipment, business interruption, wasted management or other staff resources or claims of any kind against us from third parties) arising out of or in any way Connected with our use of or our inability to use any of the Products or Recommendations (even if CIS has been advised of the possibility of such damages), including without limitation any liability associated with infringement of intellectual property, defects, bugs, errors, omissions, viruses, worms, backdoors, Trojan horses or other harmful items. + GRANT OF LIMITED RIGHTS. + CIS hereby grants each user the following rights, but only so long as the user complies with all of the terms of these Agreed Terms of Use: + 1. Except to the extent that we may have received additional authorization pursuant to a written agreement with CIS, each user may download, install and use each of the Products on a single computer; + 2. Each user may print one or more copies of any Product or any component of a Product that is in a .txt, .pdf, .doc, .mcw, or .rtf format, provided that all such copies are printed in full and are kept intact, including without limitation the text of this Agreed Terms of Use in its entirety. + RETENTION OF INTELLECTUAL PROPERTY RIGHTS; LIMITATIONS ON DISTRIBUTION. + The Products are protected by copyright and other intellectual property laws and by international treaties. We acknowledge and agree that we are not acquiring title to any intellectual property rights in the Products and that full title and all ownership rights to the Products will remain the exclusive property of CIS or CIS Parties. CIS reserves all rights not expressly granted to users in the preceding section entitled "Grant of limited rights." + Subject to the paragraph entitled "Special Rules" (which includes a waiver, granted to some classes of CIS Members, of certain limitations in this paragraph), and except as we may have otherwise agreed in a written agreement with CIS, we agree that we will not (i) decompile, disassemble, reverse engineer, or otherwise attempt to derive the source code for any software Product that is not already in the form of source code; (ii) distribute, redistribute, encumber, sell, rent, lease, lend, sublicense, or otherwise transfer or exploit rights to any Product or any component of a Product; (iii) post any Product or any component of a Product on any website, bulletin board, ftp server, newsgroup, or other similar mechanism or device, without regard to whether such mechanism or device is internal or external, (iv) remove or alter trademark, logo, copyright or other proprietary notices, legends, symbols or labels in any Product or any component of a Product; (v) remove these Agreed Terms of Use from, or alter these Agreed Terms of Use as they appear in, any Product or any component of a Product; (vi) use any Product or any component of a Product with any derivative works based directly on a Product or any component of a Product; (vii) use any Product or any component of a Product with other products or applications that are directly and specifically dependent on such Product or any component for any part of their functionality, or (viii) represent or claim a particular level of compliance with a CIS Benchmark, scoring tool or other Product. We will not facilitate or otherwise aid other individuals or entities in any of the activities listed in this paragraph. + We hereby agree to indemnify, defend, and hold CIS and all of its officers, directors, members, contributors, employees, authors, developers, agents, affiliates, licensors, information and service providers, software suppliers, hardware suppliers, and all other persons who aided CIS in the creation, development, or maintenance of the Products or Recommendations ("CIS Parties") harmless from and against any and all liability, losses, costs, and expenses (including attorneys' fees and court costs) incurred by CIS or any CIS Party in connection with any claim arising out of any violation by us of the preceding paragraph, including without limitation CIS's right, at our expense, to assume the exclusive defense and control of any matter subject to this indemnification, and in such case, we agree to cooperate with CIS in its defense of such claim. We further agree that all CIS Parties are third-party beneficiaries of our undertakings in these Agreed Terms of Use. SPECIAL RULES. + CIS has created and will from time to time create, special rules for its members and for other persons and organizations with which CIS has a written contractual relationship. Those special rules will override and supersede these Agreed Terms of Use with respect to the users who are covered by the special rules. + CIS hereby grants each CIS Security Consulting or Software Vendor Member and each CIS Organizational User Member, but only so long as such Member remains in good standing with CIS and complies with all of the terms of these Agreed Terms of Use, the right to distribute the Products and Recommendations within such Member's own organization, whether by manual or electronic means. Each such Member acknowledges and agrees that the foregoing grant is subject to the terms of such Member's membership arrangement with CIS and may, therefore, be modified or terminated by CIS at any time. + CHOICE OF LAW; JURISDICTION; VENUE. + We acknowledge and agree that these Agreed Terms of Use will be governed by and construed in accordance with the laws of the State of Maryland, that any action at law or in equity arising out of or relating to these Agreed Terms of Use shall be filed only in the courts located in the State of Maryland, that we hereby consent and submit to the personal jurisdiction of such courts for the purposes of litigating any such action. If any of these Agreed Terms of Use shall be determined to be unlawful, void, or for any reason unenforceable, then such terms shall be deemed severable and shall not affect the validity and enforceability of any remaining provisions. + BY USING THE PRODUCTS I(WE) ACKNOWLEDGE THAT WE HAVE READ THESE AGREED TERMS OF USE IN THEIR ENTIRETY, UNDERSTAND THEM, AND I(WE) AGREE TO BE BOUND BY THEM IN ALL RESPECTS. + + 3.0.0 + + Level 1 + + Items in this profile intend to: + + be practical and prudent; + provide a clear security benefit; and + not inhibit the utility of the technology beyond acceptable means. + + This profile is intended for servers. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level 2 + + This profile extends the "Level 1 - Server" profile. Items in this profile exhibit one or more of the following characteristics: + + are intended for environments or use cases where security is paramount. + acts as defense in depth measure. + may negatively inhibit the utility or performance of the technology. + + This profile is intended for servers. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure cramfs kernel module is not available + This value is used in Rule: Ensure cramfs kernel module is not available + cramfs:fs + + + Ensure freevxfs kernel module is not available + This value is used in Rule: Ensure freevxfs kernel module is not available + freevxfs:fs + + + Ensure hfs kernel module is not available + This value is used in Rule: Ensure hfs kernel module is not available + hfs:fs + + + Ensure hfsplus kernel module is not available + This value is used in Rule: Ensure hfsplus kernel module is not available + hfsplus:fs + + + Ensure jffs2 kernel module is not available + This value is used in Rule: Ensure jffs2 kernel module is not available + jffs2:fs + + + Ensure squashfs kernel module is not available + This value is used in Rule: Ensure squashfs kernel module is not available + squashfs:fs + + + Ensure udf kernel module is not available + This value is used in Rule: Ensure udf kernel module is not available + udf:fs + + + Ensure usb-storage kernel module is not available + This value is used in Rule: Ensure usb-storage kernel module is not available + usb-storage:drivers + + + Ensure /tmp is a separate partition + This value is used in Rule: Ensure /tmp is a separate partition + /tmp + + + Ensure nodev option set on /tmp partition + This value is used in Rule: Ensure nodev option set on /tmp partition + /tmp:nodev + + + Ensure nosuid option set on /tmp partition + This value is used in Rule: Ensure nosuid option set on /tmp partition + /tmp:nosuid + + + Ensure noexec option set on /tmp partition + This value is used in Rule: Ensure noexec option set on /tmp partition + /tmp:noexec + + + Ensure /dev/shm is a separate partition + This value is used in Rule: Ensure /dev/shm is a separate partition + /dev/shm + + + Ensure nodev option set on /dev/shm partition + This value is used in Rule: Ensure nodev option set on /dev/shm partition + /dev/shm:nodev + + + Ensure nosuid option set on /dev/shm partition + This value is used in Rule: Ensure nosuid option set on /dev/shm partition + /dev/shm:nosuid + + + Ensure noexec option set on /dev/shm partition + This value is used in Rule: Ensure noexec option set on /dev/shm partition + /dev/shm:noexec + + + Ensure separate partition exists for /home + This value is used in Rule: Ensure separate partition exists for /home + /home + + + Ensure nodev option set on /home partition + This value is used in Rule: Ensure nodev option set on /home partition + /home:nodev + + + Ensure nosuid option set on /home partition + This value is used in Rule: Ensure nosuid option set on /home partition + /home:nosuid + + + Ensure separate partition exists for /var + This value is used in Rule: Ensure separate partition exists for /var + /var + + + Ensure nodev option set on /var partition + This value is used in Rule: Ensure nodev option set on /var partition + /var:nodev + + + Ensure nosuid option set on /var partition + This value is used in Rule: Ensure nosuid option set on /var partition + /var:nosuid + + + Ensure separate partition exists for /var/tmp + This value is used in Rule: Ensure separate partition exists for /var/tmp + /var/tmp + + + Ensure nodev option set on /var/tmp partition + This value is used in Rule: Ensure nodev option set on /var/tmp partition + /var/tmp:nodev + + + Ensure nosuid option set on /var/tmp partition + This value is used in Rule: Ensure nosuid option set on /var/tmp partition + /var/tmp:nosuid + + + Ensure noexec option set on /var/tmp partition + This value is used in Rule: Ensure noexec option set on /var/tmp partition + /var/tmp:noexec + + + Ensure separate partition exists for /var/log + This value is used in Rule: Ensure separate partition exists for /var/log + /var/log + + + Ensure nodev option set on /var/log partition + This value is used in Rule: Ensure nodev option set on /var/log partition + /var/log:nodev + + + Ensure nosuid option set on /var/log partition + This value is used in Rule: Ensure nosuid option set on /var/log partition + /var/log:nosuid + + + Ensure noexec option set on /var/log partition + This value is used in Rule: Ensure noexec option set on /var/log partition + /var/log:noexec + + + Ensure separate partition exists for /var/log/audit + This value is used in Rule: Ensure separate partition exists for /var/log/audit + /var/log/audit + + + Ensure nodev option set on /var/log/audit partition + This value is used in Rule: Ensure nodev option set on /var/log/audit partition + /var/log/audit:nodev + + + Ensure nosuid option set on /var/log/audit partition + This value is used in Rule: Ensure nosuid option set on /var/log/audit partition + /var/log/audit:nosuid + + + Ensure noexec option set on /var/log/audit partition + This value is used in Rule: Ensure noexec option set on /var/log/audit partition + /var/log/audit:noexec + + + Ensure address space layout randomization (ASLR) is enabled + This value is used in Rule: Ensure address space layout randomization (ASLR) is enabled + kernel.randomize_va_space=2 + + + Ensure ptrace_scope is restricted + This value is used in Rule: Ensure ptrace_scope is restricted + kernel.yama.ptrace_scope=1 + + + Ensure SELinux is not disabled in bootloader configuration + This value is used in Rule: Ensure SELinux is not disabled in bootloader configuration + selinux=0 + + + Ensure SELinux is not disabled in bootloader configuration + This value is used in Rule: Ensure SELinux is not disabled in bootloader configuration + enforcing=0 + + + Ensure SELinux policy is configured + This value is used in Rule: Ensure SELinux policy is configured + ^\h*Loaded\h+policy\h+name:\h+(targeted|mls)\b + + + Ensure the SELinux mode is not disabled + This value is used in Rule: Ensure the SELinux mode is not disabled + ^Current mode:\s+(enforcing|permissive)$ + + + Ensure the SELinux mode is not disabled + This value is used in Rule: Ensure the SELinux mode is not disabled + ^Mode from config file:\s+(enforcing|permissive)$ + + + Ensure the SELinux mode is enforcing + This value is used in Rule: Ensure the SELinux mode is enforcing + ^Current mode:\s+enforcing$ + + + Ensure the SELinux mode is enforcing + This value is used in Rule: Ensure the SELinux mode is enforcing + ^Mode from config file:\s+enforcing$ + + + Ensure access to /etc/motd is configured + This value is used in Rule: Ensure access to /etc/motd is configured + /etc/motd:0133 + + + Ensure access to /etc/motd is configured + This value is used in Rule: Ensure access to /etc/motd is configured + /etc/motd/:root + + + Ensure access to /etc/motd is configured + This value is used in Rule: Ensure access to /etc/motd is configured + /etc/motd/:root + + + Ensure access to /etc/issue is configured + This value is used in Rule: Ensure access to /etc/issue is configured + /etc/issue:0133 + + + Ensure access to /etc/issue is configured + This value is used in Rule: Ensure access to /etc/issue is configured + /etc/issue/:root + + + Ensure access to /etc/issue is configured + This value is used in Rule: Ensure access to /etc/issue is configured + /etc/issue/:root + + + Ensure access to /etc/issue.net is configured + This value is used in Rule: Ensure access to /etc/issue.net is configured + /etc/issue.net:0133 + + + Ensure access to /etc/issue.net is configured + This value is used in Rule: Ensure access to /etc/issue.net is configured + /etc/issue.net/:root + + + Ensure access to /etc/issue.net is configured + This value is used in Rule: Ensure access to /etc/issue.net is configured + /etc/issue.net/:root + + + Ensure dccp kernel module is not available + This value is used in Rule: Ensure dccp kernel module is not available + dccp:net + + + Ensure tipc kernel module is not available + This value is used in Rule: Ensure tipc kernel module is not available + tipc:net + + + Ensure rds kernel module is not available + This value is used in Rule: Ensure rds kernel module is not available + rds:net + + + Ensure sctp kernel module is not available + This value is used in Rule: Ensure sctp kernel module is not available + sctp:net + + + Ensure ip forwarding is disabled + This value is used in Rule: Ensure ip forwarding is disabled + net.ipv4.ip_forward=0 + + + Ensure ip forwarding is disabled + This value is used in Rule: Ensure ip forwarding is disabled + net.ipv6.conf.all.forwarding=0 + + + Ensure packet redirect sending is disabled + This value is used in Rule: Ensure packet redirect sending is disabled + net.ipv4.conf.all.send_redirects=0 + + + Ensure packet redirect sending is disabled + This value is used in Rule: Ensure packet redirect sending is disabled + net.ipv4.conf.default.send_redirects=0 + + + Ensure bogus icmp responses are ignored + This value is used in Rule: Ensure bogus icmp responses are ignored + net.ipv4.icmp_ignore_bogus_error_responses=1 + + + Ensure broadcast icmp requests are ignored + This value is used in Rule: Ensure broadcast icmp requests are ignored + net.ipv4.icmp_echo_ignore_broadcasts=1 + + + Ensure icmp redirects are not accepted + This value is used in Rule: Ensure icmp redirects are not accepted + net.ipv4.conf.all.accept_redirects=0 + + + Ensure icmp redirects are not accepted + This value is used in Rule: Ensure icmp redirects are not accepted + net.ipv4.conf.default.accept_redirects=0 + + + Ensure icmp redirects are not accepted + This value is used in Rule: Ensure icmp redirects are not accepted + net.ipv6.conf.all.accept_redirects=0 + + + Ensure icmp redirects are not accepted + This value is used in Rule: Ensure icmp redirects are not accepted + net.ipv6.conf.default.accept_redirects=0 + + + Ensure secure icmp redirects are not accepted + This value is used in Rule: Ensure secure icmp redirects are not accepted + net.ipv4.conf.all.secure_redirects=0 + + + Ensure secure icmp redirects are not accepted + This value is used in Rule: Ensure secure icmp redirects are not accepted + net.ipv4.conf.default.secure_redirects=0 + + + Ensure reverse path filtering is enabled + This value is used in Rule: Ensure reverse path filtering is enabled + net.ipv4.conf.all.rp_filter=1 + + + Ensure reverse path filtering is enabled + This value is used in Rule: Ensure reverse path filtering is enabled + net.ipv4.conf.default.rp_filter=1 + + + Ensure source routed packets are not accepted + This value is used in Rule: Ensure source routed packets are not accepted + net.ipv4.conf.all.accept_source_route=0 + + + Ensure source routed packets are not accepted + This value is used in Rule: Ensure source routed packets are not accepted + net.ipv4.conf.default.accept_source_route=0 + + + Ensure source routed packets are not accepted + This value is used in Rule: Ensure source routed packets are not accepted + net.ipv6.conf.all.accept_source_route=0 + + + Ensure source routed packets are not accepted + This value is used in Rule: Ensure source routed packets are not accepted + net.ipv6.conf.default.accept_source_route=0 + + + Ensure suspicious packets are logged + This value is used in Rule: Ensure suspicious packets are logged + net.ipv4.conf.all.log_martians=1 + + + Ensure suspicious packets are logged + This value is used in Rule: Ensure suspicious packets are logged + net.ipv4.conf.default.log_martians=1 + + + Ensure tcp syn cookies is enabled + This value is used in Rule: Ensure tcp syn cookies is enabled + net.ipv4.tcp_syncookies=1 + + + Ensure ipv6 router advertisements are not accepted + This value is used in Rule: Ensure ipv6 router advertisements are not accepted + net.ipv6.conf.all.accept_ra=0 + + + Ensure ipv6 router advertisements are not accepted + This value is used in Rule: Ensure ipv6 router advertisements are not accepted + net.ipv6.conf.default.accept_ra=0 + + + Ensure firewalld service enabled and running + This value is used in Rule: Ensure firewalld service enabled and running + running + + + Ensure an nftables table exists + This value is used in Rule: Ensure an nftables table exists + ^\h*table\h+\H+\b(\h+.*)?$ + + + Ensure nftables base chains exist + This value is used in Rule: Ensure nftables base chains exist + ^\h*\H+\h+\H+\h+hook\h+input\b(\h+.*)?$ + + + Ensure nftables base chains exist + This value is used in Rule: Ensure nftables base chains exist + ^\h*\H+\h+\H+\h+hook\h+forward\b(\h+.*)?$ + + + Ensure nftables base chains exist + This value is used in Rule: Ensure nftables base chains exist + ^\h*\H+\h+\H+\h+hook\h+output\b(\h+.*)?$ + + + Ensure nftables default deny firewall policy + This value is used in Rule: Ensure nftables default deny firewall policy + input + + + Ensure nftables default deny firewall policy + This value is used in Rule: Ensure nftables default deny firewall policy + forward + + + Ensure nftables default deny firewall policy + This value is used in Rule: Ensure nftables default deny firewall policy + output + + + Ensure iptables loopback traffic is configured + This value is used in Rule: Ensure iptables loopback traffic is configured + ^\s*\S+\s+\S+\s+ACCEPT\s+all\s+--\s+lo\s+\*\s+0\.0\.0\.0\/0\s+0\.0\.0\.0\/0\b + + + Ensure iptables loopback traffic is configured + This value is used in Rule: Ensure iptables loopback traffic is configured + ^\s*\S+\s+\S+\s+DROP\s+all\s+--\s+\*\s+\*\s+127\.0\.0\.0\/8\s+0\.0\.0\.0\/0\b + + + Ensure iptables loopback traffic is configured + This value is used in Rule: Ensure iptables loopback traffic is configured + ^\s*\S+\s+\S+\s+ACCEPT\s+all\s+--\s+\*\s+lo\s+0\.0\.0\.0\/0\s+0\.0\.0\.0\/0\b + + + Ensure iptables default deny firewall policy + This value is used in Rule: Ensure iptables default deny firewall policy + ^Chain INPUT \(policy (DROP|REJECT)\)$ + + + Ensure iptables default deny firewall policy + This value is used in Rule: Ensure iptables default deny firewall policy + ^Chain FORWARD \(policy (DROP|REJECT)\)$ + + + Ensure iptables default deny firewall policy + This value is used in Rule: Ensure iptables default deny firewall policy + ^Chain OUTPUT \(policy (DROP|REJECT)\)$ + + + Ensure ip6tables default deny firewall policy + This value is used in Rule: Ensure ip6tables default deny firewall policy + ^\s*Chain\s+INPUT\s+\(policy (DROP|REJECT)\)(\s+.*)?$ + + + Ensure ip6tables default deny firewall policy + This value is used in Rule: Ensure ip6tables default deny firewall policy + ^\s*Chain\s+FORWARD\s+\(policy (DROP|REJECT)\)(\s+.*)?$ + + + Ensure ip6tables default deny firewall policy + This value is used in Rule: Ensure ip6tables default deny firewall policy + ^\s*Chain\s+OUTPUT\s+\(policy (DROP|REJECT)\)(\s+.*)?$ + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.allow:0137 + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.allow:root + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.allow:(root|daemon) + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.deny:0137 + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.deny:root + + + Ensure at is restricted to authorized users + This value is used in Rule: Ensure at is restricted to authorized users + /etc/at.deny:(root|daemon) + + + Ensure sshd access is configured + This value is used in Rule: Ensure sshd access is configured + allowusers,allowgroups,denyusers,denygroups:\H+\b(\h+\H+\b)* + + + Ensure sshd Banner is configured + This value is used in Rule: Ensure sshd Banner is configured + banner:\/\H+ + + + Ensure sshd Ciphers are configured + This value is used in Rule: Ensure sshd Ciphers are configured + ciphers:3des-cbc,aes128-cbc,aes192-cbc,aes256-cbc,arcfour,arcfour128,arcfour256,blowfish-cbc,cast128-cbc,rijndael-cbc@lysator\.liu\.se + + + Ensure sshd ClientAliveInterval and ClientAliveCountMax are configured + This value is used in Rule: Ensure sshd ClientAliveInterval and ClientAliveCountMax are configured + clientalivecountmax:[1-9][0-9]*m? + + + Ensure sshd ClientAliveInterval and ClientAliveCountMax are configured + This value is used in Rule: Ensure sshd ClientAliveInterval and ClientAliveCountMax are configured + ClientAliveInterval:[1-9][0-9]*m? + + + Ensure sshd DisableForwarding is enabled + This value is used in Rule: Ensure sshd DisableForwarding is enabled + disableforwarding:yes + + + Ensure sshd GSSAPIAuthentication is disabled + This value is used in Rule: Ensure sshd GSSAPIAuthentication is disabled + gssapiauthentication:no\b + + + Ensure sshd HostbasedAuthentication is disabled + This value is used in Rule: Ensure sshd HostbasedAuthentication is disabled + hostbasedauthentication:no + + + Ensure sshd IgnoreRhosts is enabled + This value is used in Rule: Ensure sshd IgnoreRhosts is enabled + ignorerhosts:yes\b + + + Ensure sshd KexAlgorithms is configured + This value is used in Rule: Ensure sshd KexAlgorithms is configured + kexalgorithms:diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1 + + + Ensure sshd LoginGraceTime is configured + This value is used in Rule: Ensure sshd LoginGraceTime is configured + logingracetime:\b([1-9]|[1-5][0-9]|60)\b + + + Ensure sshd LogLevel is configured + This value is used in Rule: Ensure sshd LogLevel is configured + loglevel:(VERBOSE|INFO)\b + + + Ensure sshd MACs are configured + This value is used in Rule: Ensure sshd MACs are configured + macs:hmac-md5,hmac-md5-96,hmac-ripemd160,hmac-sha1-96,umac-64@openssh\.com,hmac-md5-etm@openssh\.com,hmac-md5-96-etm@openssh\.com,hmac-ripemd160-etm@openssh\.com,hmac-sha1-etm@openssh\.com,hmac-sha1-96-etm@openssh\.com,umac-64-etm@openssh\.com,umac-128-etm@openssh\.com + + + Ensure sshd MaxAuthTries is configured + This value is used in Rule: Ensure sshd MaxAuthTries is configured + maxauthtries:[0-4]\b + + + Ensure sshd MaxSessions is configured + This value is used in Rule: Ensure sshd MaxSessions is configured + maxsessions:([1-9]|10)\b + + + Ensure sshd MaxStartups is configured + This value is used in Rule: Ensure sshd MaxStartups is configured + maxstartups:(10|[1-9])\H(30|[1-2][0-9]|[1-9])\H(60|[1-5][0-9]|[1-9])\b + + + Ensure sshd PermitEmptyPasswords is disabled + This value is used in Rule: Ensure sshd PermitEmptyPasswords is disabled + permitemptypasswords:no\b + + + Ensure sshd PermitRootLogin is disabled + This value is used in Rule: Ensure sshd PermitRootLogin is disabled + permitrootlogin:no\b + + + Ensure sshd PermitUserEnvironment is disabled + This value is used in Rule: Ensure sshd PermitUserEnvironment is disabled + permituserenvironment:no\b + + + Ensure sshd UsePAM is enabled + This value is used in Rule: Ensure sshd UsePAM is enabled + usepam:yes\b + + + Ensure latest version of pam is installed + This value is used in Rule: Ensure latest version of pam is installed + pam-1.1.8-23 + + + Ensure default group for the root account is GID 0 + This value is used in Rule: Ensure default group for the root account is GID 0 + root:0 + + + Ensure auditing for processes that start prior to auditd is enabled + This value is used in Rule: Ensure auditing for processes that start prior to auditd is enabled + audit=1 + + + Ensure audit_backlog_limit is sufficient + This value is used in Rule: Ensure audit_backlog_limit is sufficient + audit_backlog_limit=\d+ + + + Ensure changes to system administration scope (sudoers) is collected + This value is used in Rule: Ensure changes to system administration scope (sudoers) is collected + ^\h*-w\h+\/etc\/sudoers\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure changes to system administration scope (sudoers) is collected + This value is used in Rule: Ensure changes to system administration scope (sudoers) is collected + ^\h*-w\h+\/etc\/sudoers.d\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure actions as another user are always logged + This value is used in Rule: Ensure actions as another user are always logged + ^\h*-a\h+(?:always,exit|exit,always)\h+-F\h*arch=b32\h*(?!(?:\2\3))((?:-C\h+(?:euid!=uid|uid!=euid)\h*)|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*)|-S\h+execve\h*)(?!(?:\1\3))((?:-C\h+(?:euid!=uid|uid!=euid)\h*)|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*)|-S\h+execve\h*)(?!(?:\1\2))((?:-C\h+(?:euid!=uid|uid!=euid)\h*)|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*)|-S\h+execve\h*)(?:-k\h+\H+\b|-F\h*key=\H+\b)\h*(#[^\n\r]+)?$ + + + Ensure actions as another user are always logged + This value is used in Rule: Ensure actions as another user are always logged + ^\h*-a\h+(?:always,exit|exit,always)\h+-F\h*arch=b64\h*(?!(?:\2\3))((?:-C\h+(?:euid!=uid|uid!=euid)\h*)|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*)|-S\h+execve\h*)(?!(?:\1\3))((?:-C\h+(?:euid!=uid|uid!=euid)\h*)|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*)|-S\h+execve\h*)(?!(?:\1\2))((?:-C\h+(?:euid!=uid|uid!=euid)\h*)|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*)|-S\h+execve\h*)(?:-k\h+\H+\b|-F\h*key=\H+\b)\h*(#[^\n\r]+)?$ + + + Ensure events that modify date and time information are collected + This value is used in Rule: Ensure events that modify date and time information are collected + ^\h*-w\h+\/etc\/localtime\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify date and time information are collected + This value is used in Rule: Ensure events that modify date and time information are collected + -a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime -F key=time-change + + + Ensure events that modify date and time information are collected + This value is used in Rule: Ensure events that modify date and time information are collected + -a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -F key=time-change + + + Ensure events that modify date and time information are collected + This value is used in Rule: Ensure events that modify date and time information are collected + -a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime,stime -k time-change + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + ^\h*-w\h+\/etc\/issue\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + ^\h*-w\h+\/etc\/issue\.net\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + ^\h*-w\h+\/etc\/hosts\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + ^\h*-w\h+\/etc\/sysconfig\/network\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + ^\h*-w\h+\/etc\/sysconfig\/network-scripts\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -a always,exit -F arch=b32 -S sethostname,setdomainname -k system-locale + + + Ensure events that modify the system's network environment are collected + This value is used in Rule: Ensure events that modify the system's network environment are collected + -a always,exit -F arch=b64 -S sethostname,setdomainname -k system-locale + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b32\h+-S\h+([^#\n\r]+,)?open((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EACCES|-EACCES=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b32\h+-S\h+([^#\n\r]+,)?truncate((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EACCES|-EACCES=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b32\h+-S\h+([^#\n\r]+,)?ftruncate((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EACCES|-EACCES=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b32\h+-S\h+([^#\n\r]+,)?creat((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EACCES|-EACCES=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b32\h+-S\h+([^#\n\r]+,)?openat((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EACCES|-EACCES=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b32\h+-S\h+([^#\n\r]+,)?open((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EPERM|-EPERM=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b32\h+-S\h+([^#\n\r]+,)?truncate((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EPERM|-EPERM=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b32\h+-S\h+([^#\n\r]+,)?ftruncate((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EPERM|-EPERM=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b32\h+-S\h+([^#\n\r]+,)?creat((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EPERM|-EPERM=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b32\h+-S\h+([^#\n\r]+,)?openat((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EPERM|-EPERM=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b64\h+-S\h+([^#\n\r]+,)?open((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EACCES|-EACCES=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b64\h+-S\h+([^#\n\r]+,)?truncate((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EACCES|-EACCES=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b64\h+-S\h+([^#\n\r]+,)?ftruncate((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EACCES|-EACCES=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b64\h+-S\h+([^#\n\r]+,)?creat((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EACCES|-EACCES=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b64\h+-S\h+([^#\n\r]+,)?openat((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EACCES|-EACCES=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b64\h+-S\h+([^#\n\r]+,)?open((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EPERM|-EPERM=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b64\h+-S\h+([^#\n\r]+,)?truncate((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EPERM|-EPERM=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b64\h+-S\h+([^#\n\r]+,)?ftruncate((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EPERM|-EPERM=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b64\h+-S\h+([^#\n\r]+,)?creat((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EPERM|-EPERM=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure unsuccessful file access attempts are collected + This value is used in Rule: Ensure unsuccessful file access attempts are collected + ^\h*-a\h+(always,exit|exit,always)\h+-F\h+arch=b64\h+-S\h+([^#\n\r]+,)?openat((,\H+)+|(\h+-S\h+\H+)+)?\h+-F\h+(exit=-EPERM|-EPERM=exit)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + ^\h*-w\h+\/etc\/group\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + ^\h*-w\h+\/etc\/passwd\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + ^\h*-w\h+\/etc\/gshadow\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + ^\h*-w\h+\/etc\/shadow\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify user/group information are collected + This value is used in Rule: Ensure events that modify user/group information are collected + ^\h*-w\h+\/etc\/security\/opasswd\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure discretionary access control permission modification events are collected + This value is used in Rule: Ensure discretionary access control permission modification events are collected + -a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod + + + Ensure discretionary access control permission modification events are collected + This value is used in Rule: Ensure discretionary access control permission modification events are collected + -a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod + + + Ensure discretionary access control permission modification events are collected + This value is used in Rule: Ensure discretionary access control permission modification events are collected + -a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod + + + Ensure discretionary access control permission modification events are collected + This value is used in Rule: Ensure discretionary access control permission modification events are collected + -a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod + + + Ensure discretionary access control permission modification events are collected + This value is used in Rule: Ensure discretionary access control permission modification events are collected + -a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod + + + Ensure discretionary access control permission modification events are collected + This value is used in Rule: Ensure discretionary access control permission modification events are collected + -a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod + + + Ensure successful file system mounts are collected + This value is used in Rule: Ensure successful file system mounts are collected + -a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=unset -k mounts + + + Ensure successful file system mounts are collected + This value is used in Rule: Ensure successful file system mounts are collected + -a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=unset -k mounts + + + Ensure session initiation information is collected + This value is used in Rule: Ensure session initiation information is collected + ^\h*-w\h+\/var\/run\/utmp\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure session initiation information is collected + This value is used in Rule: Ensure session initiation information is collected + ^\h*-w\h+\/var\/run\/utmp\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure session initiation information is collected + This value is used in Rule: Ensure session initiation information is collected + ^\h*-w\h+\/var\/log\/wtmp\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure login and logout events are collected + This value is used in Rule: Ensure login and logout events are collected + ^\h*-w\h+\/var\/log\/lastlog\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure login and logout events are collected + This value is used in Rule: Ensure login and logout events are collected + ^\h*-w\h+\/var\/run\/faillock\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure file deletion events by users are collected + This value is used in Rule: Ensure file deletion events by users are collected + -a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -k delete + + + Ensure file deletion events by users are collected + This value is used in Rule: Ensure file deletion events by users are collected + -a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -k delete + + + Ensure events that modify the system's Mandatory Access Controls are collected + This value is used in Rule: Ensure events that modify the system's Mandatory Access Controls are collected + ^\h*-w\h+\/etc\/selinux\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure events that modify the system's Mandatory Access Controls are collected + This value is used in Rule: Ensure events that modify the system's Mandatory Access Controls are collected + ^\h*-w\h+\/usr/share\/selinux\/?\h+-p\h+wa\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure successful and unsuccessful attempts to use the chcon command are recorded + This value is used in Rule: Ensure successful and unsuccessful attempts to use the chcon command are recorded + ^\h*-a\h+(?:exit,always|always,exit)\h+(?:-S\h+all\h+)?(?:-F\h+path=\/usr\/bin\/chcon(?:\/)?)\h+(?!(?:\2\3))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?!(?:\1\3))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?!(?:\1\2))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?:-k\h+\H+\b|-F\h*key=\H+\b)\h*(#[^\n\r]+)?$ + + + Ensure successful and unsuccessful attempts to use the setfacl command are recorded + This value is used in Rule: Ensure successful and unsuccessful attempts to use the setfacl command are recorded + ^\h*-a\h+(?:exit,always|always,exit)\h+(?:-S\h+all\h+)?(?:-F\h+path=\/usr\/bin\/setfacl(?:\/)?)\h+(?!(?:\2\3))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?!(?:\1\3))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?!(?:\1\2))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?:-k\h+\H+\b|-F\h*key=\H+\b)\h*(#[^\n\r]+)?$ + + + Ensure successful and unsuccessful attempts to use the chacl command are recorded + This value is used in Rule: Ensure successful and unsuccessful attempts to use the chacl command are recorded + ^\h*-a\h+(?:exit,always|always,exit)\h+(?:-S\h+all\h+)?(?:-F\h+path=\/usr\/bin\/chacl(?:\/)?)\h+(?!(?:\2\3))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?!(?:\1\3))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?!(?:\1\2))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?:-k\h+\H+\b|-F\h*key=\H+\b)\h*(#[^\n\r]+)?$ + + + Ensure successful and unsuccessful attempts to use the usermod command are recorded + This value is used in Rule: Ensure successful and unsuccessful attempts to use the usermod command are recorded + ^\h*-a\h+(?:exit,always|always,exit)\h+(?:-S\h+all\h+)?(?:-F\h+path=\/usr\/sbin\/usermod(?:\/)?)\h+(?!(?:\2\3))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?!(?:\1\3))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?!(?:\1\2))(-F\h+perm=x\h*|-F\h+auid>=1000\h*|(?:-F\h+(?:auid!=(?:unset|-1|4294967295)|(?:unset|-1|4294967295)!=auid)\h*))(?:-k\h+\H+\b|-F\h*key=\H+\b)\h*(#[^\n\r]+)?$ + + + Ensure kernel module loading unloading and modification is collected + This value is used in Rule: Ensure kernel module loading unloading and modification is collected + -a always,exit -F arch=b64 -S create_module,init_module,delete_module,query_module,finit_module -F auid>=1000 -F auid!=-1 -F key=kernel_modules + + + Ensure kernel module loading unloading and modification is collected + This value is used in Rule: Ensure kernel module loading unloading and modification is collected + -a always,exit -F arch=b32 -S create_module,init_module,delete_module,query_module,finit_module -F auid>=1000 -F auid!=-1 -F key=kernel_modules + + + Ensure kernel module loading unloading and modification is collected + This value is used in Rule: Ensure kernel module loading unloading and modification is collected + ^\h*-a\h+(always,exit|exit,always)\h+(-S\h+all\h+)?-F\h+path=\/usr\/bin\/kmod\/?\h+-F\h+(perm=x|x=perm)\h+-F\h+auid>=1000\h+-F\h+(auid!=(unset|-1|4294967295)|(unset|-1|4294967295)!=auid)\h+(-k\h+\H+|-F\h*key=\H+)\h*(#.*)?$ + + + Ensure the audit configuration is immutable + This value is used in Rule: Ensure the audit configuration is immutable + ^\s*-e\s+2\b + + + Ensure audit configuration files are owned by root + This value is used in Rule: Ensure audit configuration files are owned by root + /etc/audit/:root:*.conf + + + Ensure audit configuration files are owned by root + This value is used in Rule: Ensure audit configuration files are owned by root + /etc/audit/:root:*.rules + + + Ensure permissions on /etc/passwd are configured + This value is used in Rule: Ensure permissions on /etc/passwd are configured + /etc/passwd:0133 + + + Ensure permissions on /etc/passwd are configured + This value is used in Rule: Ensure permissions on /etc/passwd are configured + /etc/passwd:root + + + Ensure permissions on /etc/passwd are configured + This value is used in Rule: Ensure permissions on /etc/passwd are configured + /etc/passwd:root + + + Ensure permissions on /etc/passwd- are configured + This value is used in Rule: Ensure permissions on /etc/passwd- are configured + /etc/passwd-:0133 + + + Ensure permissions on /etc/passwd- are configured + This value is used in Rule: Ensure permissions on /etc/passwd- are configured + /etc/passwd-:root + + + Ensure permissions on /etc/passwd- are configured + This value is used in Rule: Ensure permissions on /etc/passwd- are configured + /etc/passwd-:root + + + Ensure permissions on /etc/group are configured + This value is used in Rule: Ensure permissions on /etc/group are configured + /etc/group:0133 + + + Ensure permissions on /etc/group are configured + This value is used in Rule: Ensure permissions on /etc/group are configured + /etc/group:root + + + Ensure permissions on /etc/group are configured + This value is used in Rule: Ensure permissions on /etc/group are configured + /etc/group:root + + + Ensure permissions on /etc/group- are configured + This value is used in Rule: Ensure permissions on /etc/group- are configured + /etc/group-:0133 + + + Ensure permissions on /etc/group- are configured + This value is used in Rule: Ensure permissions on /etc/group- are configured + /etc/group-:root + + + Ensure permissions on /etc/group- are configured + This value is used in Rule: Ensure permissions on /etc/group- are configured + /etc/group-:root + + + Ensure permissions on /etc/shadow are configured + This value is used in Rule: Ensure permissions on /etc/shadow are configured + /etc/shadow:0777 + + + Ensure permissions on /etc/shadow are configured + This value is used in Rule: Ensure permissions on /etc/shadow are configured + /etc/shadow:root + + + Ensure permissions on /etc/shadow are configured + This value is used in Rule: Ensure permissions on /etc/shadow are configured + /etc/shadow:root + + + Ensure permissions on /etc/shadow- are configured + This value is used in Rule: Ensure permissions on /etc/shadow- are configured + /etc/shadow-:0777 + + + Ensure permissions on /etc/shadow- are configured + This value is used in Rule: Ensure permissions on /etc/shadow- are configured + /etc/shadow-:root + + + Ensure permissions on /etc/shadow- are configured + This value is used in Rule: Ensure permissions on /etc/shadow- are configured + /etc/shadow-:root + + + Ensure permissions on /etc/gshadow are configured + This value is used in Rule: Ensure permissions on /etc/gshadow are configured + /etc/gshadow:0777 + + + Ensure permissions on /etc/gshadow are configured + This value is used in Rule: Ensure permissions on /etc/gshadow are configured + /etc/gshadow:root + + + Ensure permissions on /etc/gshadow are configured + This value is used in Rule: Ensure permissions on /etc/gshadow are configured + /etc/gshadow:root + + + Ensure permissions on /etc/gshadow- are configured + This value is used in Rule: Ensure permissions on /etc/gshadow- are configured + /etc/gshadow-:0777 + + + Ensure permissions on /etc/gshadow- are configured + This value is used in Rule: Ensure permissions on /etc/gshadow- are configured + /etc/gshadow-:root + + + Ensure permissions on /etc/gshadow- are configured + This value is used in Rule: Ensure permissions on /etc/gshadow- are configured + /etc/gshadow-:root + + + Ensure permissions on /etc/shells are configured + This value is used in Rule: Ensure permissions on /etc/shells are configured + /etc/shells:0133 + + + Ensure permissions on /etc/shells are configured + This value is used in Rule: Ensure permissions on /etc/shells are configured + /etc/shells:root + + + Ensure permissions on /etc/shells are configured + This value is used in Rule: Ensure permissions on /etc/shells are configured + /etc/shells:root + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd:0177 + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd:root + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd:root + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd.old:0177 + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd.old:root + + + Ensure permissions on /etc/security/opasswd are configured + This value is used in Rule: Ensure permissions on /etc/security/opasswd are configured + /etc/security/opasswd.old:root + + + Ensure autofs services are not in use + This value is used in Rule: Ensure autofs services are not in use + enabled + + + Ensure autofs services are not in use + This value is used in Rule: Ensure autofs services are not in use + active + + + Ensure avahi daemon services are not in use + This value is used in Rule: Ensure avahi daemon services are not in use + enabled + + + Ensure avahi daemon services are not in use + This value is used in Rule: Ensure avahi daemon services are not in use + active + + + Ensure avahi daemon services are not in use + This value is used in Rule: Ensure avahi daemon services are not in use + enabled + + + Ensure avahi daemon services are not in use + This value is used in Rule: Ensure avahi daemon services are not in use + active + + + Ensure dhcp server services are not in use + This value is used in Rule: Ensure dhcp server services are not in use + enabled + + + Ensure dhcp server services are not in use + This value is used in Rule: Ensure dhcp server services are not in use + active + + + Ensure dhcp server services are not in use + This value is used in Rule: Ensure dhcp server services are not in use + enabled + + + Ensure dhcp server services are not in use + This value is used in Rule: Ensure dhcp server services are not in use + active + + + Ensure dns server services are not in use + This value is used in Rule: Ensure dns server services are not in use + enabled + + + Ensure dns server services are not in use + This value is used in Rule: Ensure dns server services are not in use + active + + + Ensure dnsmasq services are not in use + This value is used in Rule: Ensure dnsmasq services are not in use + enabled + + + Ensure dnsmasq services are not in use + This value is used in Rule: Ensure dnsmasq services are not in use + active + + + Ensure samba file server services are not in use + This value is used in Rule: Ensure samba file server services are not in use + enabled + + + Ensure samba file server services are not in use + This value is used in Rule: Ensure samba file server services are not in use + active + + + Ensure ftp server services are not in use + This value is used in Rule: Ensure ftp server services are not in use + enabled + + + Ensure ftp server services are not in use + This value is used in Rule: Ensure ftp server services are not in use + active + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + enabled + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + active + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + enabled + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + active + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + enabled + + + Ensure message access server services are not in use + This value is used in Rule: Ensure message access server services are not in use + active + + + Ensure network file system services are not in use + This value is used in Rule: Ensure network file system services are not in use + enabled + + + Ensure network file system services are not in use + This value is used in Rule: Ensure network file system services are not in use + active + + + Ensure nis server services are not in use + This value is used in Rule: Ensure nis server services are not in use + enabled + + + Ensure nis server services are not in use + This value is used in Rule: Ensure nis server services are not in use + active + + + Ensure print server services are not in use + This value is used in Rule: Ensure print server services are not in use + enabled + + + Ensure print server services are not in use + This value is used in Rule: Ensure print server services are not in use + active + + + Ensure print server services are not in use + This value is used in Rule: Ensure print server services are not in use + enabled + + + Ensure print server services are not in use + This value is used in Rule: Ensure print server services are not in use + active + + + Ensure rpcbind services are not in use + This value is used in Rule: Ensure rpcbind services are not in use + enabled + + + Ensure rpcbind services are not in use + This value is used in Rule: Ensure rpcbind services are not in use + active + + + Ensure rpcbind services are not in use + This value is used in Rule: Ensure rpcbind services are not in use + enabled + + + Ensure rpcbind services are not in use + This value is used in Rule: Ensure rpcbind services are not in use + active + + + Ensure rsync services are not in use + This value is used in Rule: Ensure rsync services are not in use + enabled + + + Ensure rsync services are not in use + This value is used in Rule: Ensure rsync services are not in use + active + + + Ensure rsync services are not in use + This value is used in Rule: Ensure rsync services are not in use + enabled + + + Ensure rsync services are not in use + This value is used in Rule: Ensure rsync services are not in use + active + + + Ensure snmp services are not in use + This value is used in Rule: Ensure snmp services are not in use + enabled + + + Ensure snmp services are not in use + This value is used in Rule: Ensure snmp services are not in use + active + + + Ensure telnet server services are not in use + This value is used in Rule: Ensure telnet server services are not in use + enabled + + + Ensure telnet server services are not in use + This value is used in Rule: Ensure telnet server services are not in use + active + + + Ensure tftp server services are not in use + This value is used in Rule: Ensure tftp server services are not in use + enabled + + + Ensure tftp server services are not in use + This value is used in Rule: Ensure tftp server services are not in use + active + + + Ensure tftp server services are not in use + This value is used in Rule: Ensure tftp server services are not in use + enabled + + + Ensure tftp server services are not in use + This value is used in Rule: Ensure tftp server services are not in use + active + + + Ensure web proxy server services are not in use + This value is used in Rule: Ensure web proxy server services are not in use + enabled + + + Ensure web proxy server services are not in use + This value is used in Rule: Ensure web proxy server services are not in use + active + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + enabled + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + active + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + enabled + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + active + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + enabled + + + Ensure web server services are not in use + This value is used in Rule: Ensure web server services are not in use + active + + + Ensure xinetd services are not in use + This value is used in Rule: Ensure xinetd services are not in use + enabled + + + Ensure xinetd services are not in use + This value is used in Rule: Ensure xinetd services are not in use + active + + + Ensure bluetooth services are not in use + This value is used in Rule: Ensure bluetooth services are not in use + enabled + + + Ensure bluetooth services are not in use + This value is used in Rule: Ensure bluetooth services are not in use + active + + + Ensure firewalld service enabled and running + This value is used in Rule: Ensure firewalld service enabled and running + enabled + + + Ensure nftables service is enabled and active + This value is used in Rule: Ensure nftables service is enabled and active + enabled + + + Ensure nftables service is enabled and active + This value is used in Rule: Ensure nftables service is enabled and active + active + + + Ensure iptables service is enabled and active + This value is used in Rule: Ensure iptables service is enabled and active + enabled + + + Ensure iptables service is enabled and active + This value is used in Rule: Ensure iptables service is enabled and active + active + + + Ensure ip6tables is enabled and active + This value is used in Rule: Ensure ip6tables is enabled and active + enabled + + + Ensure ip6tables is enabled and active + This value is used in Rule: Ensure ip6tables is enabled and active + active + + + Ensure cron daemon is enabled and active + This value is used in Rule: Ensure cron daemon is enabled and active + enabled + + + Ensure cron daemon is enabled and active + This value is used in Rule: Ensure cron daemon is enabled and active + active + + + Ensure rsyslog service is enabled + This value is used in Rule: Ensure rsyslog service is enabled + enabled + + + Ensure journald is configured to send logs to rsyslog + This value is used in Rule: Ensure journald is configured to send logs to rsyslog + loaded + + + Ensure journald is configured to send logs to rsyslog + This value is used in Rule: Ensure journald is configured to send logs to rsyslog + active + + + Ensure journald is configured to send logs to rsyslog + This value is used in Rule: Ensure journald is configured to send logs to rsyslog + loaded + + + Ensure journald is configured to send logs to rsyslog + This value is used in Rule: Ensure journald is configured to send logs to rsyslog + active + + + Ensure systemd-journal-remote is enabled + This value is used in Rule: Ensure systemd-journal-remote is enabled + enabled + + + Ensure journald is not configured to receive logs from a remote client + This value is used in Rule: Ensure journald is not configured to receive logs from a remote client + enabled + + + Ensure journald service is enabled + This value is used in Rule: Ensure journald service is enabled + static + + + Ensure auditd service is enabled + This value is used in Rule: Ensure auditd service is enabled + enabled + + + Ensure filesystem integrity is regularly checked + This value is used in Rule: Ensure filesystem integrity is regularly checked + enabled + + + Ensure filesystem integrity is regularly checked + This value is used in Rule: Ensure filesystem integrity is regularly checked + enabled + + + Ensure filesystem integrity is regularly checked + This value is used in Rule: Ensure filesystem integrity is regularly checked + aidecheck.service + + + Ensure password expiration is 365 days or less + This value is used in Rule: Ensure password expiration is 365 days or less + 365 + + + Ensure password expiration is 365 days or less + This value is used in Rule: Ensure password expiration is 365 days or less + 0 + + + Ensure password expiration warning days is 7 or more + This value is used in Rule: Ensure password expiration warning days is 7 or more + 7 + + + Ensure inactive password lock is 30 days or less + This value is used in Rule: Ensure inactive password lock is 30 days or less + 30 + + + Ensure inactive password lock is 30 days or less + This value is used in Rule: Ensure inactive password lock is 30 days or less + 0 + + + Ensure root password is set + This value is used in Rule: Ensure root password is set + ^\$(y|[56]|g) + + + Initial Setup + + Items in this section are advised for all systems, but may be difficult or require extensive preparation after the initial setup of the system. + + + Filesystem + + The file system is generally a built-in layer used to handle the data management of the storage. + + + Configure Filesystem Kernel Modules + + A number of uncommon filesystem types are supported under Linux. Removing support for unneeded filesystem types reduces the local attack surface of the system. If a filesystem type is not needed it should be disabled. Native Linux file systems are designed to ensure that built-in security controls function as expected. Non-native filesystems can lead to unexpected consequences to both the security and functionality of the system and should be used with caution. Many filesystems are created for niche use cases and are not maintained and supported as the operating systems are updated and patched. Users of non-native filesystems should ensure that there is attention and ongoing support for them, especially in light of frequent operating system changes. + Standard network connectivity and Internet access to cloud storage may make the use of non-standard filesystem formats to directly attach heterogeneous devices much less attractive. + + Note +: This should not be considered a comprehensive list of filesystems. You may wish to consider additions to those listed here for your environment. For the current available file system modules on the system see /usr/lib/modules/$(uname -r)/kernel/fs + + Start up scripts + +Kernel modules loaded directly via insmod + will ignore what is configured in the relevant /etc/modprobe.d/*.conf + files. If modules are still being loaded after a reboot whilst having the correctly configured blacklist + and install + command, check for insmod + entries in start up scripts such as .bashrc +. + +You may also want to check /lib/modprobe.d/ +. Please note that this directory should not be used for user defined module loading. Ensure that all such entries resides in /etc/modprobe.d/*.conf + files. + Return values + +Using /bin/false + as the command in disabling a particular module serves two purposes; to convey the meaning of the entry to the user and cause a non-zero return value. The latter can be tested for in scripts. Please note that insmod + will ignore what is configured in the relevant /etc/modprobe.d/*.conf + files. The preferred way to load modules is with modprobe +. + + + Ensure cramfs kernel module is not available + + +The cramfs + filesystem type is a compressed read-only Linux filesystem embedded in small footprint systems. A cramfs + image can be used without having to first decompress the image. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to disable the cramfs + module: + + -IF- + the module is available in the running kernel: + + +Create a file ending in .conf + with install cramfs /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist cramfs + in the /etc/modprobe.d/ + directory + +Unload cramfs + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file ending in .conf + with blacklist cramfs + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="cramfs" # set module name
+ l_mtype="fs" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure freevxfs kernel module is not available + + +The freevxfs + filesystem type is a free version of the Veritas type filesystem. This is the primary filesystem type for HP-UX operating systems. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to disable the freevxfs + module: + + -IF- + the module is available in the running kernel: + + +Create a file ending in .conf + with install freevxfs /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist freevxfs + in the /etc/modprobe.d/ + directory + +Unload freevxfs + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file ending in .conf + with blacklist freevxfs + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="freevxfs" # set module name
+ l_mtype="fs" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure hfs kernel module is not available + + +The hfs + filesystem type is a hierarchical filesystem that allows you to mount Mac OS filesystems. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to disable the hfs + module: + + -IF- + the module is available in the running kernel: + + +Create a file ending in .conf + with install hfs /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist hfs + in the /etc/modprobe.d/ + directory + +Unload hfs + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file ending in .conf + with blacklist hfs + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="hfs" # set module name
+ l_mtype="fs" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure hfsplus kernel module is not available + + +The hfsplus + filesystem type is a hierarchical filesystem designed to replace hfs + that allows you to mount Mac OS filesystems. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to disable the hfsplus + module: + + -IF- + the module is available in the running kernel: + + +Create a file ending in .conf + with install hfsplus /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist hfsplus + in the /etc/modprobe.d/ + directory + +Unload hfsplus + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file ending in .conf + with blacklist hfsplus + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="hfsplus" # set module name
+ l_mtype="fs" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure jffs2 kernel module is not available + + +The jffs2 + (journaling flash filesystem 2) filesystem type is a log-structured filesystem used in flash memory devices. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to disable the jffs2 + module: + + -IF- + the module is available in the running kernel: + + +Create a file ending in .conf + with install jffs2 /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist jffs2 + in the /etc/modprobe.d/ + directory + +Unload jffs2 + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file ending in .conf + with blacklist jffs2 + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="jffs2" # set module name
+ l_mtype="fs" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure squashfs kernel module is not available + + +The squashfs + filesystem type is a compressed read-only Linux filesystem embedded in small footprint systems. A squashfs + image can be used without having to first decompress the image. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to disable the squashfs + module: + + -IF- + the module is available in the running kernel: + + +Create a file ending in .conf + with install squashfs /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist squashfs + in the /etc/modprobe.d/ + directory + +Unload squashfs + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file ending in .conf + with blacklist squashfs + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="squashfs" # set module name
+ l_mtype="fs" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+ Impact: + + +As Snap packages utilizes squashfs + as a compressed filesystem, disabling squashfs + will cause Snap packages to fail. + + Snap + application packages of software are self-contained and work across a range of Linux distributions. This is unlike traditional Linux package management approaches, like APT or RPM, which require specifically adapted packages per Linux distribution on an application update and delay therefore application deployment from developers to their software's end-user. Snaps themselves have no dependency on any external store ("App store"), can be obtained from any source and can be therefore used for upstream software deployment. + +
+
+
+ + + + + + + +
+ + Ensure udf kernel module is not available + + +The udf + filesystem type is the universal disk format used to implement ISO/IEC 13346 and ECMA-167 specifications. This is an open vendor filesystem type for data storage on a broad range of media. This filesystem type is necessary to support writing DVDs and newer optical disc formats. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Removing support for unneeded filesystem types reduces the local attack surface of the system. If this filesystem type is not needed, disable it. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following script to disable the udf + module: + + -IF- + the module is available in the running kernel: + + +Create a file ending in .conf + with install udf /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist udf + in the /etc/modprobe.d/ + directory + +Unload udf + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file ending in .conf + with blacklist udf + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="udf" # set module name
+ l_mtype="fs" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+ Impact: + + +Microsoft Azure requires the usage of udf +. + + udf + should not + be disabled on systems run on Microsoft Azure. + +
+
+
+ + + + + + + + + +
+ + Ensure usb-storage kernel module is not available + + USB storage provides a means to transfer and store files ensuring persistence and availability of the files independent of network connection status. Its popularity and utility has led to USB-based malware being a simple and common means for network infiltration and a first step to establishing a persistent threat within a networked environment. + + + + + + + Devices + Protect + + + + + + Data + Protect + + + + + An alternative solution to disabling the usb-storage module may be found in USBGuard. + Use of USBGuard and construction of USB device policies should be done in alignment with site policy. + + + + Restricting USB access on the system will decrease the physical attack surface for a device and diminish the possible vectors to introduce malware. + + + + NIST SP 800-53 Rev. 5: SI-3 + + + + +Run the following script to disable the usb-storage + module: + + -IF- + the module is available in the running kernel: + + +Create a file ending in .conf + with install usb-storage /bin/false + in the /etc/modprobe.d/ + directory + +Create a file ending in .conf + with blacklist usb-storage + in the /etc/modprobe.d/ + directory + +Unload usb-storage + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file ending in .conf + with blacklist usb-storage + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="usb-storage" # set module name
+ l_mtype="drivers" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+ Impact: + + +Disabling the usb-storage + module will disable any usage of USB storage devices. + +If requirements and local site policy allow the use of such devices, other solutions should be configured accordingly instead. One example of a commonly used solution is USBGuard +. + +
+
+
+ + + + + + + +
+
+ + Configure Filesystem Partitions + + Directories that are used for system-wide functions can be further protected by placing them on separate partitions. This provides protection for resource exhaustion and enables the use of mounting options that are applicable to the directory's intended use. Users' data can be stored on separate partitions and have stricter mount options. A user partition is a filesystem that has been established for use by the users and does not contain software for system operations. + The recommendations in this section are easier to perform during initial system installation. If the system is already installed, it is recommended that a full backup be performed before repartitioning the system. + + Note: + + + The recommendations in this section are easier to perform during initial system installation. If the system is already installed, it is recommended that a full backup be performed before repartitioning the system + + -IF- + you are repartitioning a system that has already been installed (This may require the system to be in single-user mode): + + +Mount the new partition to a temporary mountpoint e.g. mount /dev/sda2 /mnt + + +Copy data from the original partition to the new partition. e.g. cp -a /var/tmp/* /mnt + + +Verify that all data is present on the new partition. e.g. ls -la /mnt + + +Unmount the new partition. e.g. umount /mnt + + +Remove the data from the original directory that was in the old partition. e.g. rm -Rf /var/tmp/* + Otherwise it will still consume space in the old partition that will be masked when the new filesystem is mounted. + +Mount the new partition to the desired mountpoint. e.g. mount /dev/sda2 /var/tmp + + +Update /etc/fstab + with the new mountpoint. e.g. /dev/sda2 /var/tmp xfs defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + + + + + + Configure /tmp + + +The /tmp + directory is a world-writable directory used to store data used by the system and user applications for a short period of time. This data should have no expectation of surviving a reboot, as this directory is intended to be emptied after each reboot. + + + Ensure /tmp is a separate partition + + +The /tmp + directory is a world-writable directory used for temporary storage by all users and some applications. + + -IF- + an entry for /tmp + exists in /etc/fstab + it will take precedence over entries in systemd default unit file. + + Note: + In an environment where the main system is diskless and connected to iSCSI, entries in /etc/fstab + may not take precedence. + + /tmp + can be configured to use tmpfs +. + + tmpfs + puts everything into the kernel internal caches and grows and shrinks to accommodate the files it contains and is able to swap unneeded pages out to swap space. It has maximum size limits which can be adjusted on the fly via mount -o remount +. + +Since tmpfs lives completely in the page cache and on swap, all tmpfs pages will be shown as "Shmem" in /proc/meminfo + and "Shared" in free +. Notice that these counters also include shared memory. The most reliable way to get the count is using df + and du +. + tmpfs has three mount options for sizing: + + + size +: The limit of allocated bytes for this tmpfs instance. The default is half of your physical RAM without swap. If you oversize your tmpfs instances the machine will deadlock since the OOM handler will not be able to free that memory. + + nr_blocks +: The same as size, but in blocks of PAGE_SIZE. + + nr_inodes +: The maximum number of inodes for this instance. The default is half of the number of your physical RAM pages, or (on a machine with highmem) the number of lowmem RAM pages, whichever is the lower. + + +These parameters accept a suffix k, m or g and can be changed on remount. The size parameter also accepts a suffix % +to limit this tmpfs instance to that percentage of your physical RAM. The default, when neither size + nor nr_blocks + is specified, is size=50% +. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Making /tmp + its own file system allows an administrator to set additional mount options such as the noexec + option on the mount, making /tmp + useless for an attacker to install executable code. It would also prevent an attacker from establishing a hard link to a system setuid + program and wait for it to be updated. Once the program was updated, the hard link would be broken, and the attacker would have his own copy of the program. If the program happened to have a security vulnerability, the attacker could continue to exploit the known flaw. + +This can be accomplished by either mounting tmpfs + to /tmp +, or creating a separate partition for /tmp +. + + + + https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems/ + https://www.freedesktop.org/software/systemd/man/systemd-fstab-generator.html + https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt + NIST SP 800-53 Rev. 5: CM-7 + + + + +Create or update an entry for /tmp + in /etc/fstab +: + _ Example:_ + tmpfs /tmp tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /tmp + + # mount -o remount,noexec,nodev,nosuid /tmp + + + - OR - + if /tmp + is not already mounted, run the following command to mount /tmp +: + # mount /tmp + + Impact: + + +By design files saved to /tmp + should have no expectation of surviving a reboot of the system. tmpfs + is ram based and all files stored to tmpfs + will be lost when the system is rebooted. + +If files need to be persistent through a reboot, they should be saved to /var/tmp + not /tmp +. + +Since the /tmp + directory is intended to be world-writable, there is a risk of resource exhaustion if it is not bound to tmpfs + or a separate partition. + +Running out of /tmp + space is a problem regardless of what kind of filesystem lies under it, but in a configuration where /tmp + is not a separate file system it will essentially have the whole disk available, as the default installation only creates a single / + partition. On the other hand, a RAM-based /tmp + (as with tmpfs +) will almost certainly be much smaller, which can lead to applications filling up the filesystem much more easily. Another alternative is to create a dedicated partition for /tmp + from a separate volume or disk. One of the downsides of a disk-based dedicated partition is that it will be slower than tmpfs + which is RAM-based. + + + + + + + + + + + + + + Ensure nodev option set on /tmp partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Since the /tmp + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: CM-7 + + + + + - IF - + a separate partition exists for /tmp +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /tmp + partition. + + Example: + + <device> /tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /tmp + with the configured options: + # mount -o remount /tmp + + + + + + + + + + + + + + Ensure nosuid option set on /tmp partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /tmp + filesystem is only intended for temporary file storage, set this option to ensure that users cannot create setuid + files in /tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /tmp +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /tmp + partition. + Example: + <device> /tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /tmp + with the configured options: + # mount -o remount /tmp + + + + + + + + + + + + + + Ensure noexec option set on /tmp partition + + +The noexec + mount option specifies that the filesystem cannot contain executable binaries. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /tmp + filesystem is only intended for temporary file storage, set this option to ensure that users cannot run executable binaries from /tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /tmp +. + +Edit the /etc/fstab + file and add noexec + to the fourth field (mounting options) for the /tmp + partition. + Example: + <device> /tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /tmp + with the configured options: + # mount -o remount /tmp + + Impact: + + +Setting the noexec + option on /tmp + may prevent installation and/or updating of some 3rd party software. + + + + + + + + + + + + + + + Configure /dev/shm + + +The /dev/shm + directory is a world-writable directory that can function as shared memory that facilitates inter process communication (IPC) + + + Ensure /dev/shm is a separate partition + + +The /dev/shm + directory is a world-writable directory that can function as shared memory that facilitates inter process communication (IPC). + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Making /dev/shm + its own file system allows an administrator to set additional mount options such as the noexec + option on the mount, making /dev/shm + useless for an attacker to install executable code. It would also prevent an attacker from establishing a hard link to a system setuid + program and wait for it to be updated. Once the program was updated, the hard link would be broken and the attacker would have his own copy of the program. If the program happened to have a security vulnerability, the attacker could continue to exploit the known flaw. + +This can be accomplished by mounting tmpfs + to /dev/shm +. + + + + https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems/ + https://www.freedesktop.org/software/systemd/man/systemd-fstab-generator.html + NIST SP 800-53 Rev. 5: CM-7 + + + + +For specific configuration requirements of the /dev/shm + mount for your environment, modify /etc/fstab +. + + Example: + + tmpfs /dev/shm tmpfs defaults,rw,nosuid,nodev,noexec,relatime,size=2G 0 0 + + Impact: + + +Since the /dev/shm + directory is intended to be world-writable, there is a risk of resource exhaustion if it is not bound to a separate partition. + + /dev/shm + utilizing tmpfs + can be resized using the size={size} + parameter in the relevant entry in /etc/fstab +. + + + + + + + + + + + + + + Ensure nodev option set on /dev/shm partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +Some distributions mount /dev/shm + through other means and require /dev/shm + to be added to /etc/fstab + even though it is already being mounted on boot. Others may configure /dev/shm + in other locations and may override /etc/fstab + configuration. Consult the documentation appropriate for your distribution. + + + + +Since the /dev/shm + filesystem is not intended to support devices, set this option to ensure that users cannot attempt to create special devices in /dev/shm + partitions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /dev/shm +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /dev/shm + partition. See the fstab(5) + manual page for more information. + + Example: + + tmpfs /dev/shm tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /dev/shm + with the configured options: + # mount -o remount /dev/shm + + + Note: + It is recommended to use tmpfs + as the device/filesystem type as /dev/shm + is used as shared memory space by applications. + + + + + + + + + + + + + Ensure nosuid option set on /dev/shm partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +Some distributions mount /dev/shm + through other means and require /dev/shm + to be added to /etc/fstab + even though it is already being mounted on boot. Others may configure /dev/shm + in other locations and may override /etc/fstab + configuration. Consult the documentation appropriate for your distribution. + + + + Setting this option on a file system prevents users from introducing privileged programs onto the system and allowing non-root users to execute them. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /dev/shm +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /dev/shm + partition. See the fstab(5) + manual page for more information. + + Example: + + tmpfs /dev/shm tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /dev/shm + with the configured options: + # mount -o remount /dev/shm + + + Note: + It is recommended to use tmpfs + as the device/filesystem type as /dev/shm + is used as shared memory space by applications. + + + + + + + + + + + + + Ensure noexec option set on /dev/shm partition + + +The noexec + mount option specifies that the filesystem cannot contain executable binaries. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Setting this option on a file system prevents users from executing programs from shared memory. This deters users from introducing potentially malicious software on the system. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /dev/shm +. + +Edit the /etc/fstab + file and add noexec + to the fourth field (mounting options) for the /dev/shm + partition. + + Example: + + tmpfs /dev/shm tmpfs defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /dev/shm + with the configured options: + # mount -o remount /dev/shm + + + Note: + It is recommended to use tmpfs + as the device/filesystem type as /dev/shm + is used as shared memory space by applications. + + + + + + + + + + + + + + Configure /home + + +Please note that home directories could be mounted anywhere and are not necessarily restricted to /home +, nor restricted to a single location, nor is the name restricted in any way. + +Checks can be made by looking in /etc/passwd +, looking over the mounted file systems with mount + or querying the relevant database with getent +. + + + Ensure separate partition exists for /home + + +The /home + directory is used to support disk storage needs of local users. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +When modifying /home + it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multi-user mode. + + + + +The reasoning for mounting /home + on a separate partition is as follows. + Protection from resource exhaustion + +The default installation only creates a single / + partition. Since the /home + directory contains user generated data, there is a risk of resource exhaustion. It will essentially have the whole disk available to fill up and impact the system as a whole. In addition, other operations on the system could fill up the disk unrelated to /home + and impact all local users. + Fine grained control over the mount + +Configuring /home + as its own file system allows an administrator to set additional mount options such as noexec/nosuid/nodev +. These options limit an attacker's ability to create exploits on the system. In the case of /home + options such as usrquota/grpquota + may be considered to limit the impact that users can have on each other with regards to disk resource exhaustion. Other options allow for specific behavior. See man mount + for exact details regarding filesystem-independent and filesystem-specific options. + Protection of user data + +As /home + contains user data, care should be taken to ensure the security and integrity of the data and mount point. + + + + AJ Lewis, "LVM HOWTO", http://tldp.org/HOWTO/LVM-HOWTO/ + NIST SP 800-53 Rev. 5: CM-7 + + + + +For new installations, during installation create a custom partition setup and specify a separate partition for /home +. + +For systems that were previously installed, create a new partition and configure /etc/fstab + as appropriate. + Impact: + + Resizing filesystems is a common activity in cloud-hosted servers. Separate filesystem partitions may prevent successful resizing, or may require the installation of additional tools solely for the purpose of resizing operations. The use of these additional tools may introduce their own security considerations. + + + + + + + + + + + + + + Ensure nodev option set on /home partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /home + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /home +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /home +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /home + partition. + Example: + <device> /home <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /home + with the configured options: + # mount -o remount /home + + + + + + + + + + + + + + Ensure nosuid option set on /home partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /home + filesystem is only intended for user file storage, set this option to ensure that users cannot create setuid + files in /home +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /home +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /home + partition. + Example: + <device> /home <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /home + with the configured options: + # mount -o remount /home + + + + + + + + + + + + + + + Configure /var + + +The /var + directory is used by daemons and other system services to temporarily store dynamic data. Some directories created by these processes may be world-writable. + + + Ensure separate partition exists for /var + + +The /var + directory is used by daemons and other system services to temporarily store dynamic data. Some directories created by these processes may be world-writable. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +When modifying /var + it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multi-user mode. + + + + +The reasoning for mounting /var + on a separate partition is as follows. + Protection from resource exhaustion + +The default installation only creates a single / + partition. Since the /var + directory may contain world-writable files and directories, there is a risk of resource exhaustion. It will essentially have the whole disk available to fill up and impact the system as a whole. In addition, other operations on the system could fill up the disk unrelated to /var + and cause unintended behavior across the system as the disk is full. See man auditd.conf + for details. + Fine grained control over the mount + +Configuring /var + as its own file system allows an administrator to set additional mount options such as noexec/nosuid/nodev +. These options limits an attackers ability to create exploits on the system. Other options allow for specific behavior. See man mount + for exact details regarding filesystem-independent and filesystem-specific options. + Protection from exploitation + +An example of exploiting /var + may be an attacker establishing a hard-link to a system setuid + program and wait for it to be updated. Once the program was updated, the hard-link would be broken and the attacker would have his own copy of the program. If the program happened to have a security vulnerability, the attacker could continue to exploit the known flaw. + + + + AJ Lewis, "LVM HOWTO", http://tldp.org/HOWTO/LVM-HOWTO/ + NIST SP 800-53 Rev. 5: CM-7 + + + + +For new installations, during installation create a custom partition setup and specify a separate partition for /var +. + +For systems that were previously installed, create a new partition and configure /etc/fstab + as appropriate. + Impact: + + Resizing filesystems is a common activity in cloud-hosted servers. Separate filesystem partitions may prevent successful resizing, or may require the installation of additional tools solely for the purpose of resizing operations. The use of these additional tools may introduce their own security considerations. + + + + + + + + + + + + + + Ensure nodev option set on /var partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /var +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /var + partition. + Example: + <device> /var <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var + with the configured options: + # mount -o remount /var + + + + + + + + + + + + + + Ensure nosuid option set on /var partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var + filesystem is only intended for variable files such as logs, set this option to ensure that users cannot create setuid + files in /var +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /var + partition. + Example: + <device> /var <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var + with the configured options: + # mount -o remount /var + + + + + + + + + + + + + + + Configure /var/tmp + + +The /var/tmp + directory is a world-writable directory used for temporary storage by all users and some applications. Temporary files residing in /var/tmp + are to be preserved between reboots. + + + Ensure separate partition exists for /var/tmp + + +The /var/tmp + directory is a world-writable directory used for temporary storage by all users and some applications. Temporary files residing in /var/tmp + are to be preserved between reboots. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +When modifying /var/tmp + it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multi-user mode. + + + + +The reasoning for mounting /var/tmp + on a separate partition is as follows. + Protection from resource exhaustion + +The default installation only creates a single / + partition. Since the /var/tmp + directory may contain world-writable files and directories, there is a risk of resource exhaustion. It will essentially have the whole disk available to fill up and impact the system as a whole. In addition, other operations on the system could fill up the disk unrelated to /var/tmp + and cause potential disruption to daemons as the disk is full. + Fine grained control over the mount + +Configuring /var/tmp + as its own file system allows an administrator to set additional mount options such as noexec/nosuid/nodev +. These options limits an attackers ability to create exploits on the system. Other options allow for specific behavior. See man mount + for exact details regarding filesystem-independent and filesystem-specific options. + Protection from exploitation + +An example of exploiting /var/tmp + may be an attacker establishing a hard-link to a system setuid + program and wait for it to be updated. Once the program was updated, the hard-link would be broken and the attacker would have his own copy of the program. If the program happened to have a security vulnerability, the attacker could continue to exploit the known flaw. + + + + AJ Lewis, "LVM HOWTO", http://tldp.org/HOWTO/LVM-HOWTO/ + NIST SP 800-53 Rev. 5: CM-7 + + + + +For new installations, during installation create a custom partition setup and specify a separate partition for /var/tmp +. + +For systems that were previously installed, create a new partition and configure /etc/fstab + as appropriate. + Impact: + + Resizing filesystems is a common activity in cloud-hosted servers. Separate filesystem partitions may prevent successful resizing, or may require the installation of additional tools solely for the purpose of resizing operations. The use of these additional tools may introduce their own security considerations. + + + + + + + + + + + + + + Ensure nodev option set on /var/tmp partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/tmp + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /var/tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/tmp +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /var/tmp + partition. + Example: + <device> /var/tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/tmp + with the configured options: + # mount -o remount /var/tmp + + + + + + + + + + + + + + Ensure nosuid option set on /var/tmp partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/tmp + filesystem is only intended for temporary file storage, set this option to ensure that users cannot create setuid + files in /var/tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/tmp +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /var/tmp + partition. + Example: + <device> /var/tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/tmp + with the configured options: + # mount -o remount /var/tmp + + + + + + + + + + + + + + Ensure noexec option set on /var/tmp partition + + +The noexec + mount option specifies that the filesystem cannot contain executable binaries. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/tmp + filesystem is only intended for temporary file storage, set this option to ensure that users cannot run executable binaries from /var/tmp +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/tmp +. + +Edit the /etc/fstab + file and add noexec + to the fourth field (mounting options) for the /var/tmp + partition. + Example: + <device> /var/tmp <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/tmp + with the configured options: + # mount -o remount /var/tmp + + + + + + + + + + + + + + + Configure /var/log + + +The /var/log + directory is used by system services to store log data. + + + Ensure separate partition exists for /var/log + + +The /var/log + directory is used by system services to store log data. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +When modifying /var/log + it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multiuser mode. + + + + +The reasoning for mounting /var/log + on a separate partition is as follows. + Protection from resource exhaustion + +The default installation only creates a single / + partition. Since the /var/log + directory contains log files which can grow quite large, there is a risk of resource exhaustion. It will essentially have the whole disk available to fill up and impact the system as a whole. + Fine grained control over the mount + +Configuring /var/log + as its own file system allows an administrator to set additional mount options such as noexec/nosuid/nodev +. These options limit an attackers ability to create exploits on the system. Other options allow for specific behavior. See man mount + for exact details regarding filesystem-independent and filesystem-specific options. + Protection of log data + +As /var/log + contains log files, care should be taken to ensure the security and integrity of the data and mount point. + + + + AJ Lewis, "LVM HOWTO", http://tldp.org/HOWTO/LVM-HOWTO/ + NIST SP 800-53 Rev. 5: CM-7 + + + + +For new installations, during installation create a custom partition setup and specify a separate partition for /var/log + . + +For systems that were previously installed, create a new partition and configure /etc/fstab + as appropriate. + Impact: + + Resizing filesystems is a common activity in cloud-hosted servers. Separate filesystem partitions may prevent successful resizing, or may require the installation of additional tools solely for the purpose of resizing operations. The use of these additional tools may introduce their own security considerations. + + + + + + + + + + + + + + Ensure nodev option set on /var/log partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /var/log +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /var/log + partition. + Example: + <device> /var/log <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log + with the configured options: + # mount -o remount /var/log + + + + + + + + + + + + + + Ensure nosuid option set on /var/log partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log + filesystem is only intended for log files, set this option to ensure that users cannot create setuid + files in /var/log +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /var/log + partition. + Example: + <device> /var/log <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log + with the configured options: + # mount -o remount /var/log + + + + + + + + + + + + + + Ensure noexec option set on /var/log partition + + +The noexec + mount option specifies that the filesystem cannot contain executable binaries. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log + filesystem is only intended for log files, set this option to ensure that users cannot run executable binaries from /var/log +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log +. + +Edit the /etc/fstab + file and add noexec + to the fourth field (mounting options) for the /var/log + partition. + Example: + <device> /var/log <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log + with the configured options: + # mount -o remount /var/log + + + + + + + + + + + + + + + Configure /var/log/audit + + +The auditing daemon, auditd +, stores log data in the /var/log/audit + directory. + + + Ensure separate partition exists for /var/log/audit + + +The auditing daemon, auditd +, stores log data in the /var/log/audit + directory. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +When modifying /var/log/audit + it is advisable to bring the system to emergency mode (so auditd is not running), rename the existing directory, mount the new file system, and migrate the data over before returning to multi-user mode. + + + + +The reasoning for mounting /var/log/audit + on a separate partition is as follows. + Protection from resource exhaustion + +The default installation only creates a single / + partition. Since the /var/log/audit + directory contains the audit.log + file which can grow quite large, there is a risk of resource exhaustion. It will essentially have the whole disk available to fill up and impact the system as a whole. In addition, other operations on the system could fill up the disk unrelated to /var/log/audit + and cause auditd + to trigger it's space_left_action + as the disk is full. See man auditd.conf + for details. + Fine grained control over the mount + +Configuring /var/log/audit + as its own file system allows an administrator to set additional mount options such as noexec/nosuid/nodev +. These options limit an attacker's ability to create exploits on the system. Other options allow for specific behavior. See man mount + for exact details regarding filesystem-independent and filesystem-specific options. + Protection of audit data + +As /var/log/audit + contains audit logs, care should be taken to ensure the security and integrity of the data and mount point. + + + + AJ Lewis, "LVM HOWTO", http://tldp.org/HOWTO/LVM-HOWTO/ + NIST SP 800-53 Rev. 5: CM-7 + + + + +For new installations, during installation create a custom partition setup and specify a separate partition for /var/log/audit +. + +For systems that were previously installed, create a new partition and configure /etc/fstab + as appropriate. + Impact: + + Resizing filesystems is a common activity in cloud-hosted servers. Separate filesystem partitions may prevent successful resizing, or may require the installation of additional tools solely for the purpose of resizing operations. The use of these additional tools may introduce their own security considerations. + + + + + + + + + + + + + + Ensure nodev option set on /var/log/audit partition + + +The nodev + mount option specifies that the filesystem cannot contain special devices. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log/audit + filesystem is not intended to support devices, set this option to ensure that users cannot create a block or character special devices in /var/log/audit +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log/audit +. + +Edit the /etc/fstab + file and add nodev + to the fourth field (mounting options) for the /var/log/audit + partition. + Example: + <device> /var/log/audit <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log/audit + with the configured options: + # mount -o remount /var/log/audit + + + + + + + + + + + + + + Ensure nosuid option set on /var/log/audit partition + + +The nosuid + mount option specifies that the filesystem cannot contain setuid + files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log/audit + filesystem is only intended for variable files such as logs, set this option to ensure that users cannot create setuid + files in /var/log/audit +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log/audit +. + +Edit the /etc/fstab + file and add nosuid + to the fourth field (mounting options) for the /var/log/audit + partition. + Example: + <device> /var/log/audit <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log/audit + with the configured options: + # mount -o remount /var/log/audit + + + + + + + + + + + + + + Ensure noexec option set on /var/log/audit partition + + +The noexec + mount option specifies that the filesystem cannot contain executable binaries. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Since the /var/log/audit + filesystem is only intended for audit logs, set this option to ensure that users cannot run executable binaries from /var/log/audit +. + + + + See the fstab(5) manual page for more information. + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + + - IF - + a separate partition exists for /var/log/audit +. + +Edit the /etc/fstab + file and add noexec + to the fourth field (mounting options) for the /var/log/audit + partition. + Example: + <device> /var/log/audit <fstype> defaults,rw,nosuid,nodev,noexec,relatime 0 0 + + +Run the following command to remount /var/log/audit + with the configured options: + # mount -o remount /var/log/audit + + + + + + + + + + + + + + +
+ + Configure Software and Patch Management + + +Fedora 19/CentOS 7 stream derived Linux distributions use yum + to install and update software packages. Patch management procedures may vary widely between enterprises. Large enterprises may choose to install a local updates server that can be used in place of their distributions servers, whereas a single deployment of a system may prefer to get updates directly. Updates can be performed automatically or manually, depending on the site's policy for patch management. Organizations may prefer to test patches against their environment on a non-production system before rolling out to production. + Outdated software is vulnerable to cyber criminals and hackers. Software updates help reduce the risk to your organization. The release of software update notes often reveal the patched exploitable entry points to the public. Public knowledge of these exploits cans your organization more vulnerable to malicious actors attempting to gain entry to your system's data. + Software updates often offer new and improved features and speed enhancements + For the purpose of this benchmark, the requirement is to ensure that a patch management process is defined and maintained, the specifics of which are left to the organization. + + + Ensure GPG keys are configured + + The RPM Package Manager implements GPG key signing to verify package integrity during and after installation. + + + + + + + Applications + Protect + + + + Applications + Protect + + + + + + Applications + Protect + + + + Applications + Protect + + + + + +Fedora public keys: https://getfedora.org/security/ + + + + + It is important to ensure that updates are obtained from a valid source to protect against spoofing that could lead to the inadvertent installation of malware on the system. To this end, verify that GPG keys are configured correctly for your system. + + + + + + NIST SP 800-53 Rev. 5: SI-2 + + + + Update your package manager GPG keys in accordance with site policy. + + + + + + Ensure gpgcheck is globally activated + + +The gpgcheck + option, found in the main section of the /etc/yum.conf + and individual /etc/yum.repos.d/* + files, determines if an RPM package's signature is checked prior to its installation. + + + + + + + Applications + Protect + + + + + + Applications + Protect + + + + + + It is important to ensure that an RPM's package signature is always checked prior to installation to ensure that the software is obtained from a trusted source. + + + + NIST SP 800-53 Rev. 5: SI-2 + + + + +Edit /etc/yum.conf + and set gpgcheck=1 + in the [main] + section. + + Example: + + # sed -i 's/^gpgcheck\s*=\s*.*/gpgcheck=1/' /etc/yum.conf + + +Edit any failing files in /etc/yum.repos.d/* + and set all instances starting with gpgcheck + to 1 +. + + Example: + + # find /etc/yum.repos.d/ -name "*.repo" -exec echo "Checking:" {} \; -exec sed -ri 's/^gpgcheck\s*=\s*.*/gpgcheck=1/' {} \; + + + + + + + + + + + + + + + Ensure repo_gpgcheck is globally activated + + +The repo_gpgcheck + option, found in the main section of the /etc/yum.conf + and individual /etc/yum.repos.d/* + files, will perform a GPG signature check on the repodata. + + + + + + + Applications + Protect + + + + + + Applications + Protect + + + + + + It is important to ensure that the repository data signature is always checked prior to installation to ensure that the software is not tampered with in any way. + + + + NIST SP 800-53 Rev. 5: SI-2 + + + + + Global configuration + + +Edit /etc/yum.conf + and set repo_gpgcheck=1 + in the [main] + section. + Example: + +[main]
+repo_gpgcheck=1 +
+ + Per repository configuration + + First check that the particular repository support GPG checking on the repodata. + +Edit any failing files in /etc/yum.repos.d/* + and set all instances starting with repo_gpgcheck + to 1 +. + Impact: + + +Not all repositories, notably RedHat, support repo_gpgcheck +. Take care to set this value to false (default) for particular repositories that do not support it. If enabled on repositories that do not support repo_gpgcheck + installation of packages will fail. + +Research is required by the user to determine which repositories is configured on the local system and, from that list, which support repo_gpgcheck +. + +
+
+
+
+ + Ensure package manager repositories are configured + + Systems need to have the respective package manager repositories configured to ensure that the system is able to receive the latest patches and updates. + + + + + + + Applications + Protect + + + + Applications + Protect + + + + + + Applications + Protect + + + + Applications + Protect + + + + + +For further information about Fedora repositories see: https://docs.fedoraproject.org/en-US/quick-docs/repositories/ + + + + + If a system's package repositories are misconfigured, important patches may not be identified or a rogue repository could introduce compromised software. + + + + + + NIST SP 800-53 Rev. 5: SI-2 + + + + Configure your package manager repositories according to site policy. + + + + + + Ensure updates, patches, and additional security software are installed + + Periodically patches are released for included software either due to security flaws or to include additional functionality. + + + + + + + Applications + Protect + + + + Applications + Protect + + + + + + Applications + Protect + + + + + Site policy may mandate a testing period before install onto production systems for available updates. + # yum check-update + + + + + Newer patches may contain security enhancements that would not be available through the latest full update. As a result, it is recommended that the latest software patches be used to take advantage of the latest functionality. As with any software installation, organizations need to determine if a given update meets their requirements and verify the compatibility and supportability of any additional software against the update revision that is selected. + + + + + NIST SP 800-53 Rev. 5: SI-2 + + + + Use your package manager to update all packages on the system according to site policy. + The following command will install all available updates: + # yum update + + Once the update process is complete, verify if reboot is required to load changes. + needs-restarting -r + + + + + +
+ + Configure Secure Boot Settings + + The recommendations in this section focus on securing the bootloader and settings involved in the boot process directly. + + Note: + + + +In Fedora 28 based distributions, the kernel command-line parameters for systems using the GRUB2 bootloader were defined in the kernelopts + environment variable. This variable was stored in the /boot/grub2/grubenv + file for each kernel boot entry. However, storing the kernel command-line parameters using kernelopts + was not robust. Therefore, the kernelopts + has been removed and the kernel command-line parameters are now stored in the Boot Loader Specification (BLS) snippet, instead of in the /boot/loader/entries/<KERNEL_BOOT_ENTRY>.conf + file. + +Boot loader configuration files are unified across CPU architectures + + +Configuration files for the GRUB boot loader are now stored in the /boot/grub2/ + directory on all supported CPU architectures. The /boot/efi/EFI/redhat/grub.cfg + file, which GRUB previously used as the main configuration file on UEFI systems, now simply loads the /boot/grub2/grub.cfg + file. + +This change simplifies the layout of the GRUB configuration file, improves user experience, and provides the following notable benefits: + + You can boot the same installation with either EFI or legacy BIOS. + You can use the same documentation and commands for all architectures. + GRUB configuration tools are more robust, because they no longer rely on symbolic links and they do not have to handle platform-specific cases. + The usage of the GRUB configuration files is aligned with images generated by CoreOS Assembler (COSA) and OSBuild. + The usage of the GRUB configuration files is aligned with other Linux distributions. + Fedora 28 based distributions no longer boot on 32-bit UEFI + + + + + +Support for the 32-bit UEFI firmware was removed from the GRUB and shim boot loaders. As a consequence, Fedora 28 based distributions require a 64-bit UEFI, and can no longer boot on 64-bit systems that use a 32-bit UEFI. + + The following packages have been removed as part of this change: + + grub2-efi-ia32 + + + grub2-efi-ia32-cdboot + + + grub2-efi-ia32-modules + + + shim-ia32 + + + + + + Reference: + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/considerations_in_adopting_rhel_8/index#kernel_considerations-in-adopting-RHEL-8 + + + + Ensure authentication required for single user mode + + Single user mode (rescue mode) is used for recovery when the system detects an issue during boot or by manual selection from the bootloader. + + Note: + The systemctl option --fail + is synonymous with --job-mode=fail +. Using either is acceptable. + + + + + + + Applications + Protect + + + + + + Applications + Protect + + + + + + Requiring authentication in single user mode (rescue mode) prevents an unauthorized user from rebooting the system into single user to gain root privileges without credentials. + + + + + + + +Edit /usr/lib/systemd/system/rescue.service + and /usr/lib/systemd/system/emergency.service + and set ExecStart + to use /sbin/sulogin + or /usr/sbin/sulogin +: + ExecStart=-/bin/sh -c "/sbin/sulogin; /usr/bin/systemctl --fail --no-block default" + + + + + + + + + + + + + + + + Configure Additional Process Hardening + + + Ensure address space layout randomization (ASLR) is enabled + + Address space layout randomization (ASLR) is an exploit mitigation technique which randomly arranges the address space of key data areas of a process. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Randomly placing virtual memory regions will make it difficult to write memory page exploits as the memory placement will be consistently shifting. + + + + CCI-000366: The organization implements the security configuration settings + NIST SP 800-53 Rev. 5: CM-6 + NIST SP 800-53A :: CM-6.1 (iv) + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + kernel.randomize_va_space = 2 + + + + Example: + + # printf '%s\n' "kernel.randomize_va_space = 2" >> /etc/sysctl.d/60-kernel_sysctl.conf + + Run the following command to set the active kernel parameter: + # sysctl -w kernel.randomize_va_space=2 + + + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten + + + + + + + + + + + + + Ensure ptrace_scope is restricted + + +The ptrace() + system call provides a means by which one process (the "tracer") may observe and control the execution of another process (the "tracee"), and examine and change the tracee's memory and registers. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +Ptrace is very rarely used by regular applications and is mostly used by debuggers such as gdb + and strace +. + + + + If one application is compromised, it would be possible for an attacker to attach to other running processes (e.g. Bash, Firefox, SSH sessions, GPG agent, etc) to extract additional credentials and continue to expand the scope of their attack. + Enabling restricted mode will limit the ability of a compromised process to PTRACE_ATTACH on other processes running under the same user. With restricted mode, ptrace will continue to work with root user. + + + + https://www.kernel.org/doc/Documentation/security/Yama.txt + https://github.com/raj3shp/termspy + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + kernel.yama.ptrace_scope = 1 + + + + Example: + + # printf '%s\n' "kernel.yama.ptrace_scope = 1" >> /etc/sysctl.d/60-kernel_sysctl.conf + + Run the following command to set the active kernel parameter: + # sysctl -w kernel.yama.ptrace_scope=1 + + + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten + + + + + + + + + + + + + Ensure core dump backtraces are disabled + + A core dump is the memory of an executable program. It is generally used to determine why a program aborted. It can also be used to glean confidential information from a core file. + + + A core dump includes a memory image taken at the time the operating system terminates an application. The memory image could contain sensitive data and is generally useful only for developers trying to debug problems, increasing the risk to the system. + + https://www.freedesktop.org/software/systemd/man/coredump.conf.html + NIST SP 800-53 Rev. 5: CM-6b + + + + +Create or edit the file /etc/systemd/coredump.conf + and edit or add the following line: + ProcessSizeMax=0 + + + + + + + + + + + + Ensure core dump storage is disabled + + A core dump is the memory of an executable program. It is generally used to determine why a program aborted. It can also be used to glean confidential information from a core file. + + + A core dump includes a memory image taken at the time the operating system terminates an application. The memory image could contain sensitive data and is generally useful only for developers trying to debug problems. + + https://www.freedesktop.org/software/systemd/man/coredump.conf.html + + + + +Create or edit the file /etc/systemd/coredump.conf + and edit or add the following line: + Storage=none + + + + + + + + + + + + + Mandatory Access Control + + Mandatory Access Control (MAC) provides an additional layer of access restrictions to processes on top of the base Discretionary Access Controls. By restricting how processes can access files and resources on a system the potential impact from vulnerabilities in the processes can be reduced. + + Impact: + Mandatory Access Control limits the capabilities of applications and daemons on a system, while this can prevent unauthorized access the configuration of MAC can be complex and difficult to implement correctly preventing legitimate access from occurring. + + + Configure SELinux + + SELinux implements Mandatory Access Control (MAC). Every process and system resource has a special security label called an SELinux context. A SELinux context, sometimes referred to as an SELinux label, is an identifier which abstracts away the system-level details and focuses on the security properties of the entity. Not only does this provide a consistent way of referencing objects in the SELinux policy, but it also removes any ambiguity that can be found in other identification methods. For example, a file can have multiple valid path names on a system that makes use of bind mounts. + The SELinux policy uses these contexts in a series of rules which define how processes can interact with each other and the various system resources. By default, the policy does not allow any interaction unless a rule explicitly grants access. + In Fedora 28 Family Linux distributions, system services are controlled by the systemd daemon; systemd starts and stops all services, and users and processes communicate with systemd using the systemctl utility. The systemd daemon can consult the SELinux policy and check the label of the calling process and the label of the unit file that the caller tries to manage, and then ask SELinux whether or not the caller is allowed the access. This approach strengthens access control to critical system capabilities, which include starting and stopping system services. + This automatically limits the damage that the software can do to files accessible by the calling user. The user does not need to take any action to gain this benefit. For an action to occur, both the traditional DAC permissions must be satisfied as well as the SELinux MAC rules. The action will not be allowed if either one of these models does not permit the action. In this way, SELinux rules can only make a system's permissions more restrictive and secure. SELinux requires a complex policy to allow all the actions required of a system under normal operation. + Two such policies have been designed for use with Fedora 28 Family Linux distributions and are included with the system: + + + targeted + - Targeted processes run in their own domain, called a confined domain. In a confined domain, the files that a targeted process has access to are limited. If a confined process is compromised by an attacker, the attacker’s access to resources and the possible damage they can do is also limited. SELinux denies access to these resources and logs the denial. + + mls + - Implements Multi-Level Security (MLS), which introduces even more kinds of labels (sensitivity and category) and rules that govern access based on these. + + +This section provides guidance for the configuration of the targeted + policy. + + Note: + + + Remember that SELinux policy rules are checked after DAC rules. SELinux policy rules are not used if DAC rules deny access first, which means that no SELinux denial is logged if the traditional DAC rules prevent the access. + This section only applies if SELinux is in use on the system. Additional Mandatory Access Control systems exist. + To avoid incorrect SELinux labeling and subsequent problems, ensure that you start services using a systemctl start command. + + + References: + + + +NSA SELinux resources: + + + https://www.nsa.gov/Research/Technical-Papers-Brochures/smdsearch14229/selinux + + + + +Fedora SELinux resources: + + +Getting started with SELinux: https://docs.fedoraproject.org/en-US/quick-docs/getting-started-with-selinux + + +User Guide: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/using_selinux/index + + + + +SELinux Project web page and wiki: + + + http://www.selinuxproject.org + + + + + + + Ensure SELinux is installed + + SELinux provides Mandatory Access Control. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Without a Mandatory Access Control system installed only the default Discretionary Access Control system will be available. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following command to install SELinux +: + # yum install libselinux + + + + + + + + + + + + Ensure SELinux is not disabled in bootloader configuration + + Configure SELINUX to be enabled at boot time and verify that it has not been overwritten by the grub boot parameters. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + This recommendation is designed around the grub 2 bootloader, if another bootloader is in use in your environment enact equivalent settings. + + grubby + is a command line tool used to configure bootloader menu entries across multiple architectures. It is used for updating and displaying information about the configuration files for various architecture specific bootloaders. + It is primarily designed to be used from scripts which install new kernels and need to find information about the current boot environment. + The grubby executable has full support for the grub2 bootloader on x86_64 systems using legacy BIOS or modern UEFI firmware and ppc64 and ppc64le hardware using OPAL or SLOF as firmware. + Legacy s390 and the current s390x architectures and their zipl bootloader are fully supported. + Support for yaboot has been deprecated as all ppc architecture hardware since the Power8 uses grub2 or petitboot which both use the grub2 configuration file format. + Legacy bootloaders LILO, SILO, and ELILO are deprecated and no longer receiving active support in favor of previously mentioned bootloaders. + The default bootloader target is primarily determined by the architecture for which grubby has been built. Each architecture has a preferred bootloader, and each bootloader has its own configuration file. If no bootloader is selected on the command line, grubby will use these default settings to search for an existing configuration. If no bootloader configuration file is found, grubby will use the default value for that architecture. + + + + SELinux must be enabled at boot time in your grub configuration to ensure that the controls it provides are not overridden. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + GRUBBY(8) + + + + +Run the following command to remove the selinux=0 + and enforcing=0 + parameters: + grubby --update-kernel ALL --remove-args "selinux=0 enforcing=0" + + +Edit /etc/default/grub + and remove selinux=0 + and enforcing=0 + from all lines beginning with GRUB_CMDLINE_LINUX= + or GRUB_CMDLINE_LINUX_DEFAULT= +: + + Example: + + # sed -ri 's/\s*(selinux|enforcing)=0\s*//g' /etc/default/grub + + Impact: + + Files created while SELinux is disabled are not labeled at all. This behavior causes problems when changing to enforcing mode because files are labeled incorrectly or are not labeled at all. To prevent incorrectly labeled and unlabeled files from causing problems, file systems are automatically relabeled when changing from the disabled state to permissive or enforcing mode. This can be a long running process that should be accounted for as it may extend downtime during initial re-boot. + + + + + + + + + + + + + + + + + + + + + + + + Ensure SELinux policy is configured + + Configure SELinux to meet or exceed the default targeted policy, which constrains daemons and system software only. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +If your organization requires stricter policies, ensure that they are set in the /etc/selinux/config + file. + + + + Security configuration requirements vary from site to site. Some sites may mandate a policy that is stricter than the default policy, which is perfectly acceptable. This item is intended to ensure that at least the default recommendations are met. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Edit the /etc/selinux/config + file to set the SELINUXTYPE parameter: + SELINUXTYPE=targeted + + + + + + + + + + + + + + + + + Ensure the SELinux mode is not disabled + + SELinux can run in one of three modes: disabled, permissive, or enforcing: + + + Enforcing + - Is the default, and recommended, mode of operation; in enforcing mode SELinux operates normally, enforcing the loaded security policy on the entire system. + + Permissive + - The system acts as if SELinux is enforcing the loaded security policy, including labeling objects and emitting access denial entries in the logs, but it does not actually deny any operations. While not recommended for production systems, permissive mode can be helpful for SELinux policy development. + + Disabled + - Is strongly discouraged; not only does the system avoid enforcing the SELinux policy, it also avoids labeling any persistent objects such as files, making it difficult to enable SELinux in the future + + + Note: + You can set individual domains to permissive mode while the system runs in enforcing mode. For example, to make the httpd_t domain permissive: + # semanage permissive -a httpd_t + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Running SELinux in disabled mode is strongly discouraged; not only does the system avoid enforcing the SELinux policy, it also avoids labeling any persistent objects such as files, making it difficult to enable SELinux in the future. + + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/selinux_users_and_administrators_guide/sect-security-enhanced_linux-introduction-selinux_modes + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + Run one of the following commands to set SELinux's running mode: + +To set SELinux mode to Enforcing +: + # setenforce 1 + + + -OR- + + +To set SELinux mode to Permissive +: + # setenforce 0 + + +Edit the /etc/selinux/config + file to set the SELINUX parameter: + For Enforcing mode: + SELINUX=enforcing + + + -OR- + + For Permissive mode: + SELINUX=permissive + + + + + + + + + + + + + + + + + + + + + + + + Ensure the SELinux mode is enforcing + + SELinux can run in one of three modes: disabled, permissive, or enforcing: + + + Enforcing + - Is the default, and recommended, mode of operation; in enforcing mode SELinux operates normally, enforcing the loaded security policy on the entire system. + + Permissive + - The system acts as if SELinux is enforcing the loaded security policy, including labeling objects and emitting access denial entries in the logs, but it does not actually deny any operations. While not recommended for production systems, permissive mode can be helpful for SELinux policy development. + + Disabled + - Is strongly discouraged; not only does the system avoid enforcing the SELinux policy, it also avoids labeling any persistent objects such as files, making it difficult to enable SELinux in the future + + + Note: You can set individual domains to permissive mode while the system runs in enforcing mode. For example, to make the httpd_t domain permissive: + + # semanage permissive -a httpd_t + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Running SELinux in disabled mode the system not only avoids enforcing the SELinux policy, it also avoids labeling any persistent objects such as files, making it difficult to enable SELinux in the future. + Running SELinux in Permissive mode, though helpful for developing SELinux policy, only logs access denial entries, but does not deny any operations. + + + + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/selinux_users_and_administrators_guide/sect-security-enhanced_linux-introduction-selinux_modes + CCI-002165: The information system enforces organization-defined discretionary access control policies over defined subjects and objects. + NIST SP 800-53 Revision 4 :: AC-3 (4) + CCI-002696: The information system verifies correct operation of organization-defined security functions. + NIST SP 800-53 Revision 4 :: SI-6 a + + + + Run the following command to set SELinux's running mode: + # setenforce 1 + + +Edit the /etc/selinux/config + file to set the SELINUX parameter: + For Enforcing mode: + SELINUX=enforcing + + Impact: + + Running SELinux in Enforcing mode may block intended access to files or processes if the SELinux policy is not correctly configured. If this occurs, review the system logs for details and update labels or policy as appropriate. + + + + + + + + + + + + + + + + + + + + + + + + Ensure no unconfined services exist + + Unconfined processes run in unconfined domains + + + + + + + Data + Protect + + + + + + Devices + Protect + + + + + Occasionally certain daemons such as backup or centralized management software may require running unconfined. Any such software should be carefully analyzed and documented before such an exception is made. + + + + For unconfined processes, SELinux policy rules are applied, but policy rules exist that allow processes running in unconfined domains almost all access. Processes running in unconfined domains fall back to using DAC rules exclusively. If an unconfined process is compromised, SELinux does not prevent an attacker from gaining access to system resources and data, but of course, DAC rules are still used. SELinux is a security enhancement on top of DAC rules – it does not replace them + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + Investigate any unconfined processes found during the audit action. They may need to have an existing security context assigned to them or a policy built for them. + + + + + + + + + + + + Ensure the MCS Translation Service (mcstrans) is not installed + + +The mcstransd + daemon provides category label information to client processes requesting information. The label translations are defined in /etc/selinux/targeted/setrans.conf + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Since this service is not used very often, remove it to reduce the amount of potentially vulnerable code running on the system. + + + + NIST SP 800-53 Rev. 5: SI-4 + + + + +Run the following command to uninstall mcstrans +: + # yum remove mcstrans + + + + + + + + + + + + Ensure SETroubleshoot is not installed + + The SETroubleshoot service notifies desktop users of SELinux denials through a user-friendly interface. The service provides important information around configuration errors, unauthorized intrusions, and other potential errors. + + + + + + + Devices + Protect + + + + + + Data + Protect + + + + + + The SETroubleshoot service is an unnecessary daemon to have running on a server, especially if X Windows is disabled. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following command to uninstall setroubleshoot +: + # yum remove setroubleshoot + + + + + + + + + + + + + + Configure Command Line Warning Banners + + Presenting a warning message prior to the normal user login may assist in the prosecution of trespassers on the computer system. Changing some of these login banners also has the side effect of hiding OS version information and other detailed system information from attackers attempting to target specific exploits at a system. + +Guidelines published by the US Department of Defense require that warning messages include at least the name of the organization that owns the system, the fact that the system is subject to monitoring and that such monitoring is in compliance with local statutes, and that use of the system implies consent to such monitoring. It is important that the organization's legal counsel review the content of all messages before any system modifications are made, as these warning messages are inherently site-specific. More information (including citations of relevant case law) can be found at http://www.justice.gov/criminal/cybercrime/ + + +The /etc/motd +, /etc/issue +, and /etc/issue.net + files govern warning banners for standard command line logins for both local and remote users. + + Note: + The text provided in the remediation actions for these items is intended as an example only. Please edit to include the specific text for your organization as approved by your legal department. + + + Ensure message of the day is configured properly + + +The contents of the /etc/motd + file are displayed to users after login and function as a message of the day for authenticated users. + +Unix-based systems have typically displayed information about the OS release and patch level upon logging in to the system. This information can be useful to developers who are developing software for a particular OS platform. If mingetty(8) + supports the following options, they display operating system information: \m + - machine architecture \r + - operating system release \s + - operating system name \v + - operating system version + + + +Warning messages inform users who are attempting to login to the system of their legal status regarding the system and must include the name of the organization that owns the system and any monitoring policies that are in place. Displaying OS and patch level information in login banners also has the side effect of providing detailed system information to attackers attempting to target specific exploits of a system. Authorized users can easily get this information by running the " uname -a + " command once they have logged in. + + NIST SP 800-53 Rev. 5: CM-6, CM-1, CM-3 + + + + +Edit the files in /etc/update-motd.d/ + and remove any instances of \m + , \r + , \s + , \v + or references to the OS platform + + + - IF - + required by local site policy, add the site specific "Message Of The Day" to a file in /etc/update-motd.d/ +: + + Example: + + # printf '%s\n' "Authorized users only. All activity may be monitored and reported." > /etc/update-motd.d/30-banner + + + + + + + Ensure local login warning banner is configured properly + + +The contents of the /etc/issue + file are displayed to users prior to login for local terminals. + +Unix-based systems have typically displayed information about the OS release and patch level upon logging in to the system. This information can be useful to developers who are developing software for a particular OS platform. If mingetty(8) + supports the following options, they display operating system information: \m + - machine architecture \r + - operating system release \s + - operating system name \v + - operating system version - or the operating system's name + + + +Warning messages inform users who are attempting to login to the system of their legal status regarding the system and must include the name of the organization that owns the system and any monitoring policies that are in place. Displaying OS and patch level information in login banners also has the side effect of providing detailed system information to attackers attempting to target specific exploits of a system. Authorized users can easily get this information by running the " uname -a + " command once they have logged in. + + NIST SP 800-53 Rev. 5: CM-6, CM-1, CM-3 + + + + +Edit the /etc/issue + file with the appropriate contents according to your site policy, remove any instances of \m + , \r + , \s + , \v + or references to the OS platform + + + Example: + + # echo "Authorized users only. All activity may be monitored and reported." > /etc/issue + + + + + + + + + + + + + + + + Ensure remote login warning banner is configured properly + + +The contents of the /etc/issue.net + file are displayed to users prior to login for remote connections from configured services. + +Unix-based systems have typically displayed information about the OS release and patch level upon logging in to the system. This information can be useful to developers who are developing software for a particular OS platform. If mingetty(8) + supports the following options, they display operating system information: \m + - machine architecture \r + - operating system release \s + - operating system name \v + - operating system version + + + +Warning messages inform users who are attempting to login to the system of their legal status regarding the system and must include the name of the organization that owns the system and any monitoring policies that are in place. Displaying OS and patch level information in login banners also has the side effect of providing detailed system information to attackers attempting to target specific exploits of a system. Authorized users can easily get this information by running the " uname -a + " command once they have logged in. + + NIST SP 800-53 Rev. 5: CM-6, CM-1, CM-3 + + + + +Edit the /etc/issue.net + file with the appropriate contents according to your site policy, remove any instances of \m + , \r + , \s + , \v + or references to the OS platform + + + Example: + + # echo "Authorized users only. All activity may be monitored and reported." > /etc/issue.net + + + + + + + + + + + + + + + + Ensure access to /etc/motd is configured + + +The contents of the /etc/motd + file are displayed to users after login and function as a message of the day for authenticated users. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + + -IF- + the /etc/motd + file does not have the correct access configured, it could be modified by unauthorized users with incorrect or misleading information. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/motd +: + +# chown root:root $(readlink -e /etc/motd)
+# chmod u-x,go-wx $(readlink -e /etc/motd) +
+ + -OR- + + +Run the following command to remove the /etc/motd + file: + # rm /etc/motd + +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure access to /etc/issue is configured + + +The contents of the /etc/issue + file are displayed to users prior to login for local terminals. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + + -IF- + the /etc/issue + file does not have the correct access configured, it could be modified by unauthorized users with incorrect or misleading information. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/issue +: + +# chown root:root $(readlink -e /etc/issue)
+# chmod u-x,go-wx $(readlink -e /etc/issue) +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure access to /etc/issue.net is configured + + +The contents of the /etc/issue.net + file are displayed to users prior to login for remote connections from configured services. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + + -IF- + the /etc/issue.net + file does not have the correct access configured, it could be modified by unauthorized users with incorrect or misleading information. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/issue.net +: + +# chown root:root $(readlink -e /etc/issue.net)
+# chmod u-x,go-wx $(readlink -e /etc/issue.net) +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+
+ + Services + + While applying system updates and patches helps correct known vulnerabilities, one of the best ways to protect the system against as yet unreported vulnerabilities is to disable all services that are not required for normal system operation. This prevents the exploitation of vulnerabilities discovered at a later date. If a service is not enabled, it cannot be exploited. The actions in this section of the document provide guidance on some services which can be safely disabled and under which circumstances, greatly reducing the number of possible threats to the resulting system. Additionally, some services which should remain enabled but with secure configuration are covered as well as insecure service clients. + + + Configure Time Synchronization + + It is recommended that physical systems and virtual guests lacking direct access to the physical host's clock be configured to synchronize their time using a service such as NTP or chrony. + + + Ensure time synchronization is in use + + System time should be synchronized between all systems in an environment. This is typically done by establishing an authoritative time server or set of servers and having all systems synchronize their clocks to them. + + Note: + If another method for time synchronization is being used, this section may be skipped. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + On systems where host based time synchronization is not available, verify that chrony is installed. + On systems where host based time synchronization is available consult your documentation and verify that host based synchronization is in use. + + + + Time synchronization is important to support time sensitive security mechanisms like Kerberos and also ensures log files have consistent time records across the enterprise, which aids in forensic investigations. + + + + NIST SP 800-53 Rev. 5: AU-3, AU-12 + + + + +Run the following command to install chrony +: + # yum install chrony + + + + + + + + + + + + Ensure chrony is configured + + + chrony + is a daemon which implements the Network Time Protocol (NTP) and is designed to synchronize system clocks across a variety of systems and use a source that is highly accurate. More information on chrony + can be found at http://chrony.tuxfamily.org/ +. chrony + can be configured to be a client and/or a server. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +On systems where host based time synchronization is not available, verify that chrony + is installed. + On systems where host based time synchronization is available consult your documentation and verify that host based synchronization is in use. + + + + +If chrony + is in use on the system proper configuration is vital to ensuring time synchronization is working properly. + + + + NIST SP 800-53 Rev. 5: AU-3, AU-12 + + + + +Add or edit server or pool lines to a file ending in .conf + in the /etc/chrony.d/` directory as appropriate: + + Example: + + server <remote-server> + + + + + + + + + + + + + + + + + + + + Ensure chrony is not run as the root user + + +The file /etc/sysconfig/chronyd + allows configuration of options for chrony + to include the user chrony + is run as. By default this is set to the user chrony + + + + Services should not be set to run as the root user + + + + + +Edit the file /etc/sysconfig/chronyd + and add or modify the following line: + OPTIONS="-u chrony" + + +Run the following command to reload the chronyd.service + configuration: + # systemctl try-reload-or-restart chronyd.service + + + + + + + + + + + + + Configure Special Purpose Services + + This section describes services that are installed on systems that specifically need to run these services. If any of these services are not required, it is recommended that the package be removed. + + -IF- + the package is required for a dependency: + + Ensure the dependent package is approved by local site policy + Ensure stopping and masking the service and/or socket meets local site policy + Stop and mask the service and/or socket to reduce the potential attack surface + + The following commands can be used to stop and mask the service and socket: + +# systemctl stop <service_name>.socket <service_name>.service
+# systemctl mask <service_name>.socket <service_name>.service +
+ + Note: + This should not be considered a comprehensive list of services not required for normal system operation. You may wish to consider additions to those listed here for your environment +
+ + Ensure autofs services are not in use + + + autofs + allows automatic mounting of devices, typically including CD/DVDs and USB drives. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + This control should align with the tolerance of the use of portable drives and optical media in the organization. On a server requiring an admin to manually mount media can be part of defense-in-depth to reduce the risk of unapproved software or information being introduced or proprietary software or information being exfiltrated. If admins commonly use flash drives and Server access has sufficient physical controls, requiring manual mounting may not increase security. + + + + With automounting enabled anyone with physical access could attach a USB drive or disc and have its contents available in system even if they lacked permissions to mount it themselves. + + + + NIST SP 800-53 Rev. 5: SI-3, MP-7 + + + + +Run the following commands to stop autofs.service + and remove autofs + package: + +# systemctl stop autofs.service
+# yum remove autofs +
+ + -OR- + + + -IF- + the autofs + package is required as a dependency: + +Run the following commands to stop and mask autofs.service +: + +# systemctl stop autofs.service
+# systemctl mask autofs.service +
+ Impact: + + The use of portable hard drives is very common for workstation users. If your organization allows the use of portable storage or media on workstations and physical access controls to workstations is considered adequate there is little value add in turning off automounting. + +There may be packages that are dependent on the autofs + package. If the autofs + package is removed, these dependent packages will be removed as well. Before removing the autofs + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the autofs.service + leaving the autofs + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure avahi daemon services are not in use + + Avahi is a free zeroconf implementation, including a system for multicast DNS/DNS-SD service discovery. Avahi allows programs to publish and discover services and hosts running on a local network with no specific configuration. For example, a user can plug a computer into a network and Avahi automatically finds printers to print to, files to look at and people to talk to, as well as network services running on the machine. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Automatic discovery of network services is not normally required for system functionality. It is recommended to remove this package to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: SI-4 + + + + +Run the following commands to stop avahi-daemon.socket + and avahi-daemon.service +, and remove the avahi + package: + +# systemctl stop avahi-daemon.socket avahi-daemon.service
+# yum remove avahi +
+ + -OR- + + + -IF- + the avahi + package is required as a dependency: + +Run the following commands to stop and mask the avahi-daemon.socket + and avahi-daemon.service +: + +# systemctl stop avahi-daemon.socket avahi-daemon.service
+# systemctl mask avahi-daemon.socket avahi-daemon.service +
+ Impact: + + +There may be packages that are dependent on the avahi + package. If the avahi + package is removed, these dependent packages will be removed as well. Before removing the avahi + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the avahi-daemon.socket + and avahi-daemon.service + leaving the avahi + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure dhcp server services are not in use + + +The Dynamic Host Configuration Protocol (DHCP) is a service that allows machines to be dynamically assigned IP addresses. There are two versions of the DHCP protocol DHCPv4 + and DHCPv6 +. At startup the server may be started for one or the other via the -4 + or -6 + arguments. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Unless a system is specifically set up to act as a DHCP server, it is recommended that the dhcp-server + package be removed to reduce the potential attack surface. + + + + dhcpd(8) + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop dhcpd.service + and dhcpd6.service + and remove dhcp + package: + +# systemctl stop dhcpd.service dhcpd6.service
+# yum remove dhcp +
+ + -OR- + + + -IF- + the dhcp + package is required as a dependency: + +Run the following commands to stop and mask dhcpd.service + and dhcpd6.service +: + +# systemctl stop dhcpd.service dhcpd6.service
+# systemctl mask dhcpd.service dhcpd6.service +
+ Impact: + + +There may be packages that are dependent on the dhcp-server + package. If the dhcp-server + package is removed, these dependent packages will be removed as well. Before removing the dhcp-server + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the dhcpd.service + and dhcpd6.service + leaving the dhcp-server + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure dns server services are not in use + + The Domain Name System (DNS) is a hierarchical naming system that maps names to IP addresses for computers, services and other resources connected to a network. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless a system is specifically designated to act as a DNS server, it is recommended that the package be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop named.service + and remove bind + package: + +# systemctl stop named.service
+# yum remove bind +
+ + -OR- + + + -IF- + the bind + package is required as a dependency: + +Run the following commands to stop and mask named.service +: + +# systemctl stop named.service
+# systemctl mask named.service +
+ Impact: + + +There may be packages that are dependent on the bind + package. If the bind + package is removed, these dependent packages will be removed as well. Before removing the bind + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the named.service + leaving the bind + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure dnsmasq services are not in use + + + dnsmasq + is a lightweight tool that provides DNS caching, DNS forwarding and DHCP (Dynamic Host Configuration Protocol) services. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless a system is specifically designated to act as a DNS caching, DNS forwarding and/or DHCP server, it is recommended that the package be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop dnsmasq.service + and remove dnsmasq + package: + +# systemctl stop dnsmasq.service
+# yum remove dnsmasq +
+ + -OR- + + + -IF- + the dnsmasq + package is required as a dependency: + +Run the following commands to stop and mask the dnsmasq.service +: + +# systemctl stop dnsmasq.service
+# systemctl mask dnsmasq.service +
+ Impact: + + +There may be packages that are dependent on the dnsmasq + package. If the dnsmasq + package is removed, these dependent packages will be removed as well. Before removing the dnsmasq + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the dnsmasq.service + leaving the dnsmasq + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure samba file server services are not in use + + The Samba daemon allows system administrators to configure their Linux systems to share file systems and directories with Windows desktops. Samba will advertise the file systems and directories via the Server Message Block (SMB) protocol. Windows desktop users will be able to mount these directories and file systems as letter drives on their systems. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If there is no need to mount directories and file systems to Windows systems, then this package can be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-6, CM-7 + + + + +Run the following command to stop smb.service + and remove samba + package: + +# systemctl stop smb.service
+# yum remove samba +
+ + -OR- + + + -IF- + the samba + package is required as a dependency: + +Run the following commands to stop and mask the smb.service +: + +# systemctl stop smb.service
+# systemctl mask smb.service +
+ Impact: + + +There may be packages that are dependent on the samba + package. If the samba + package is removed, these dependent packages will be removed as well. Before removing the samba + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the smb.service + leaving the samba + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure ftp server services are not in use + + FTP (File Transfer Protocol) is a traditional and widely used standard tool for transferring files between a server and clients over a network, especially where no authentication is necessary (permits anonymous users to connect to a server). + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless there is a need to run the system as a FTP server, it is recommended that the package be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop vsftpd.service + and remove vsftpd + package: + +# systemctl stop vsftpd.service
+# yum remove vsftpd +
+ + -OR- + + + -IF- + the vsftpd + package is required as a dependency: + +Run the following commands to stop and mask the vsftpd.service +: + +# systemctl stop vsftpd.service
+# systemctl mask vsftpd.service +
+ + Note: + Other ftp server packages may exist. If not required and authorized by local site policy, they should also be removed. If the package is required for a dependency, the service should be stopped and masked. + Impact: + + +There may be packages that are dependent on the vsftpd + package. If the vsftpd + package is removed, these dependent packages will be removed as well. Before removing the vsftpd + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the vsftpd.service + leaving the vsftpd + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure message access server services are not in use + + + dovecot + and cyrus-imapd + are open source IMAP and POP3 server packages for Linux based systems. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless POP3 and/or IMAP servers are to be provided by this system, it is recommended that the package be removed to reduce the potential attack surface. + + Note: + Several IMAP/POP3 servers exist and can use other service names. These should also be audited and the packages removed if not required. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop dovecot.socket +, dovecot.service +, and cyrus-imapd.service +, and remove dovecot + and cyrus-imapd + packages: + +# systemctl stop dovecot.socket dovecot.service cyrus-imapd.service
+# yum remove dovecot cyrus-imapd +
+ + -OR- + + + -IF- + a package is installed and + is required for dependencies: + +Run the following commands to stop and mask dovecot.socket +, dovecot.service +, and cyrus-imapd.service +: + +# systemctl stop dovecot.socket dovecot.service cyrus-imapd.service
+# systemctl mask dovecot.socket dovecot.service cyrus-imapd.service +
+ Impact: + + +There may be packages that are dependent on dovecot + and cyrus-imapd + packages. If dovecot + and cyrus-imapd + packages are removed, these dependent packages will be removed as well. Before removing dovecot + and cyrus-imapd + packages, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask dovecot.socket +, dovecot.service + and cyrus-imapd.service + leaving dovecot + and cyrus-imapd + packages installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure network file system services are not in use + + The Network File System (NFS) is one of the first and most widely distributed file systems in the UNIX environment. It provides the ability for systems to mount file systems of other servers through the network. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + Many of the libvirt packages used by Enterprise Linux virtualization are dependent on the nfs-utils package. If the nfs-utils package is required as a dependency, the nfs-server service should be disabled and masked to reduce the attack surface of the system. + + + + +If the system does not require access to network shares or the ability to provide network file system services for other host's network shares, it is recommended that the nfs-utils + package be removed to reduce the attack surface of the system. + + + + NIST SP 800-53 Rev. 5: CM-6, CM-7 + + + + +Run the following command to stop nfs-server.service + and remove nfs-utils + package: + +# systemctl stop nfs-server.service
+# yum remove nfs-utils +
+ + -OR- + + + -IF- + the nfs-utils + package is required as a dependency: + +Run the following commands to stop and mask the nfs-server.service +: + +# systemctl stop nfs-server.service
+# systemctl mask nfs-server.service +
+ Impact: + + +Many of the libvirt + packages used by Enterprise Linux virtualization are dependent on the nfs-utils + package. If the nfs-utils + package is removed, these dependent packages will be removed as well. Before removing the nfs-utils + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the nfs-server.service + leaving the nfs-utils + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure nis server services are not in use + + +The Network Information Service (NIS), formerly known as Yellow Pages, is a client-server directory service protocol used to distribute system configuration files. The NIS client ( ypbind + ) was used to bind a machine to an NIS server and receive the distributed configuration files. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + The NIS service is inherently an insecure system that has been vulnerable to DOS attacks, buffer overflows and has poor authentication for querying NIS maps. NIS generally has been replaced by such protocols as Lightweight Directory Access Protocol (LDAP). It is recommended that the service be removed. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop ypserv.service + and remove ypserv + package: + +# systemctl stop ypserv.service
+# yum remove ypserv +
+ + -OR- + + + -IF- + the ypserv + package is required as a dependency: + +Run the following commands to stop and mask ypserv.service +: + +# systemctl stop ypserv.service
+# systemctl mask ypserv.service +
+ Impact: + + +There may be packages that are dependent on the ypserv + package. If the ypserv + package is removed, these dependent packages will be removed as well. Before removing the ypserv + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the ypserv.service + leaving the ypserv + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure print server services are not in use + + The Common Unix Print System (CUPS) provides the ability to print to both local and network printers. A system running CUPS can also accept print jobs from remote systems and print them to local printers. It also provides a web based remote administration capability. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If the system does not need to print jobs or accept print jobs from other systems, it is recommended that CUPS be removed to reduce the potential attack surface. + + + + http://www.cups.org + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop cups.socket + and cups.service +, and remove the cups + package: + +# systemctl stop cups.socket cups.service
+# yum remove cups +
+ + -OR- + + + -IF- + the cups + package is required as a dependency: + +Run the following commands to stop and mask the cups.socket + and cups.service +: + +# systemctl stop cups.socket cups.service
+# systemctl mask cups.socket cups.service +
+ Impact: + + +Removing the cups package, or disabling cups.socket + and/or cups.service + will prevent printing from the system, a common task for workstation systems. + +There may be packages that are dependent on the cups + package. If the cups + package is removed, these dependent packages will be removed as well. Before removing the cups + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask cups.socket + and cups.service + leaving the cups + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure rpcbind services are not in use + + +The rpcbind + utility maps RPC services to the ports on which they listen. RPC processes notify rpcbind + when they start, registering the ports they are listening on and the RPC program numbers they expect to serve. The client system then contacts rpcbind + on the server with a particular RPC program number. The rpcbind.service + redirects the client to the proper port number so it can communicate with the requested service. + Portmapper is an RPC service, which always listens on tcp and udp 111, and is used to map other RPC services (such as nfs, nlockmgr, quotad, mountd, etc.) to their corresponding port number on the server. When a remote host makes an RPC call to that server, it first consults with portmap to determine where the RPC server is listening. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +A small request (~82 bytes via UDP) sent to the Portmapper generates a large response (7x to 28x amplification), which makes it a suitable tool for DDoS attacks. If rpcbind + is not required, it is recommended to remove rpcbind + package to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-6, CM-7 + + + + +Run the following commands to stop rpcbind.socket + and rpcbind.service +, and remove the rpcbind + package: + +# systemctl stop rpcbind.socket rpcbind.service
+# yum remove rpcbind +
+ + -OR- + + + -IF- + the rpcbind + package is required as a dependency: + +Run the following commands to stop and mask the rpcbind.socket + and rpcbind.service +: + +# systemctl stop rpcbind.socket rpcbind.service
+# systemctl mask rpcbind.socket rpcbind.service +
+ Impact: + + +Many of the libvirt packages used by Enterprise Linux virtualization, and the nfs-utils + package used for The Network File System (NFS), are dependent on the rpcbind + package. If the rpcbind + package is removed, these dependent packages will be removed as well. Before removing the rpcbind + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the rpcbind.socket + and rpcbind.service + leaving the rpcbind + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure rsync services are not in use + + +The rsyncd.service + can be used to synchronize files between systems over network links. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Unless required, the rsync + package should be removed to reduce the potential attack surface. + +The rsyncd.service + presents a security risk as it uses unencrypted protocols for communication. + + + + NIST SP 800-53 Rev. 5: CM-6, CM-7 + + + + +Run the following commands to stop rsyncd.socket + and rsyncd.service +, and remove the rsync + package: + +# systemctl stop rsyncd.socket rsyncd.service
+# yum remove rsync +
+ + -OR- + + + -IF- + the rsync + package is required as a dependency: + +Run the following commands to stop and mask the rsyncd.socket + and rsyncd.service +: + +# systemctl stop rsyncd.socket rsyncd.service
+# systemctl mask rsyncd.socket rsyncd.service +
+ Impact: + + +There may be packages that are dependent on the rsync + package. If the rsync + package is removed, these dependent packages will be removed as well. Before removing the rsync + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the rsyncd.socket + and rsyncd.service + leaving the rsync + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure snmp services are not in use + + Simple Network Management Protocol (SNMP) is a widely used protocol for monitoring the health and welfare of network equipment, computer equipment and devices like UPSs. + Net-SNMP is a suite of applications used to implement SNMPv1 (RFC 1157), SNMPv2 (RFCs 1901-1908), and SNMPv3 (RFCs 3411-3418) using both IPv4 and IPv6. + Support for SNMPv2 classic (a.k.a. "SNMPv2 historic" - RFCs 1441-1452) was dropped with the 4.0 release of the UCD-snmp package. + The Simple Network Management Protocol (SNMP) server is used to listen for SNMP commands from an SNMP management system, execute the commands or collect the information and then send results back to the requesting system. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +The SNMP server can communicate using SNMPv1 +, which transmits data in the clear and does not require authentication to execute commands. SNMPv3 + replaces the simple/clear text password sharing used in SNMPv2 + with more securely encoded parameters. If the the SNMP service is not required, the net-snmp + package should be removed to reduce the attack surface of the system. + + Note: + If SNMP is required: + + +The server should be configured for SNMP v3 + only. User Authentication + and Message Encryption + should be configured. + +If SNMP v2 + is absolutely + necessary, modify the community strings' values. + + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop snmpd.service + and remove net-snmp + package: + +# systemctl stop snmpd.service
+# yum remove net-snmp +
+ + -OR- + If the package is required for dependencies: + +Run the following commands to stop and mask the snmpd.service +: + +# systemctl stop snmpd.service
+# systemctl mask snmpd.service +
+ Impact: + + +There may be packages that are dependent on the net-snmp + package. If the net-snmp + package is removed, these packages will be removed as well. + +Before removing the net-snmp + package, review any dependent packages to determine if they are required on the system. If a dependent package is required, stop and mask the snmpd.service + leaving the net-snmp + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure telnet server services are not in use + + +The telnet-server + package contains the telnet + daemon, which accepts connections from users from other systems via the telnet + protocol. + + + + + + + Devices + Protect + + + + + + Applications + Respond + + + + Devices + Protect + + + + + + +The telnet + protocol is insecure and unencrypted. The use of an unencrypted transmission medium could allow a user with access to sniff network traffic the ability to steal credentials. The ssh + package provides an encrypted session and stronger security. + + + + + NIST SP 800-53 Rev. 5: CM-7, CM-11 + + + + +Run the following commands to stop telnet.socket + and remove the telnet-server + package: + +# systemctl stop telnet.socket
+# yum remove telnet-server +
+ + -OR- + + + -IF- + a package is installed and + is required for dependencies: + +Run the following commands to stop and mask telnet.socket +: + +# systemctl stop telnet.socket
+# systemctl mask telnet.socket +
+ Impact: + + +There may be packages that are dependent on the telnet-server + package. If the telnet-server + package is removed, these dependent packages will be removed as well. Before removing the telnet-server + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the telnet.socket + leaving the telnet-server + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure tftp server services are not in use + + Trivial File Transfer Protocol (TFTP) is a simple protocol for exchanging files between two TCP/IP machines. TFTP servers allow connections from a TFTP Client for sending and receiving files. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless there is a need to run the system as a TFTP server, it is recommended that the package be removed to reduce the potential attack surface. + TFTP does not have built-in encryption, access control or authentication. This makes it very easy for an attacker to exploit TFTP to gain access to files + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop tftp.socket + and tftp.service +, and remove the tftp-server + package: + +# systemctl stop tftp.socket tftp.service
+# yum remove tftp-server +
+ + -OR- + + + -IF- + the tftp-server + package is required as a dependency: + +Run the following commands to stop and mask tftp.socket + and tftp.service +: + +# systemctl stop tftp.socket tftp.service
+# systemctl mask tftp.socket tftp.service +
+ Impact: + + TFTP is often used to provide files for network booting such as for PXE based installation of servers. + +There may be packages that are dependent on the tftp-server + package. If the tftp-server + package is removed, these dependent packages will be removed as well. Before removing the tftp-server + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the tftp.socket + and tftp.service + leaving the tftp-server + package installed. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure web proxy server services are not in use + + Squid is a standard proxy server used in many distributions and environments. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + Several HTTP proxy servers exist. These and other services should be checked. + + + + Unless a system is specifically set up to act as a proxy server, it is recommended that the squid package be removed to reduce the potential attack surface. + + Note: + Several HTTP proxy servers exist. These should be checked and removed unless required. + + + + NIST SP 800-53 Rev. 5: CM-6, CM-7 + + + + +Run the following commands to stop squid.service + and remove the squid + package: + +# systemctl stop squid.service
+# yum remove squid +
+ + -OR- + If the squid + package is required as a dependency: + +Run the following commands to stop and mask the squid.service +: + +# systemctl stop squid.service
+# systemctl mask squid.service +
+ Impact: + + +There may be packages that are dependent on the squid + package. If the squid + package is removed, these dependent packages will be removed as well. Before removing the squid + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the squid.service + leaving the squid + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure web server services are not in use + + Web servers provide the ability to host web site content. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless there is a local site approved requirement to run a web server service on the system, web server packages should be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop httpd.socket +, httpd.service +, and nginx.service +, and remove httpd + and nginx + packages: + +# systemctl stop httpd.socket httpd.service nginx.service
+# yum remove httpd nginx +
+ + -OR- + + + -IF- + a package is installed and + is required for dependencies: + +Run the following commands to stop and mask httpd.socket +, httpd.service +, and nginx.service +: + +# systemctl stop httpd.socket httpd.service nginx.service
+# systemctl mask httpd.socket httpd.service nginx.service +
+ + Note: + Other web server packages may exist. If not required and authorized by local site policy, they should also be removed. If the package is required for a dependency, the service and socket should be stopped and masked. + Impact: + + Removal of web server packages will remove that ability for the server to host web services. + + -IF- + the web server package is required for a dependency, any related service or socket should be stopped and masked. + + Note: + If the remediation steps to mask a service are followed and that package is not installed on the system, the service and/or socket will still be masked. If the package is installed due to an approved requirement to host a web server, the associated service and/or socket would need to be unmasked before it could be enabled and/or started. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure xinetd services are not in use + + +The eXtended InterNET Daemon ( xinetd + ) is an open source super daemon that replaced the original inetd + daemon. The xinetd + daemon listens for well known services and dispatches the appropriate daemon to properly respond to service requests. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless your organization specifically requires xinetd services, it is recommended that the package be removed to reduce the attack surface are of the system. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop xinetd.service +, and remove the xinetd + package: + +# systemctl stop xinetd.service
+# yum remove xinetd +
+ + -OR- + + + -IF- + the xinetd + package is required as a dependency: + +Run the following commands to stop and mask the xinetd.service +: + +# systemctl stop xinetd.service
+# systemctl mask xinetd.service +
+ Impact: + + +There may be packages that are dependent on the xinetd + package. If the xinetd + package is removed, these dependent packages will be removed as well. Before removing the xinetd + package, review any dependent packages to determine if they are required on the system. + -IF- a dependent package is required: stop and mask the avahi-daemon.socket and avahi-daemon.service leaving the avahi package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+ + Ensure X window server services are not in use + + The X Window System provides a Graphical User Interface (GUI) where users can have multiple windows in which to run programs and various add on. The X Windows system is typically used on workstations where users login, but not on servers where users typically do not login. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Unless your organization specifically requires graphical login access via X Windows, remove it to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-11 + + + + + -IF- + a Graphical Desktop Manager or X-Windows server is not required and approved by local site policy: + Run the following command to remove the X Windows Server packages: + # yum remove xorg-x11-server-common + + Impact: + + +If a Graphical Desktop Manager (GDM) is in use on the system, there may be a dependency on the xorg-x11-server-common + package. If the GDM is required and approved by local site policy, the package should not + be removed. + Many Linux systems run applications which require a Java runtime. Some Linux Java packages have a dependency on specific X Windows xorg-x11-fonts. One workaround to avoid this dependency is to use the "headless" Java packages for your specific Java runtime. + + + + + + + + + + + + + + + Ensure mail transfer agents are configured for local-only mode + + Mail Transfer Agents (MTA), such as sendmail and Postfix, are used to listen for incoming mail and transfer the messages to the appropriate user or mail server. If the system is not intended to be a mail server, it is recommended that the MTA be configured to only process local mail. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + The software for all Mail Transfer Agents is complex and most have a long history of security issues. While it is important to ensure that the system can process local mail messages, it is not necessary to have the MTA's daemon listening on a port unless the server is intended to be a mail server that receives and processes mail from other systems. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Edit /etc/postfix/main.cf + and add the following line to the RECEIVING MAIL section. If the line already exists, change it to look like the line below: + inet_interfaces = loopback-only + + +Run the following command to restart postfix +: + # systemctl restart postfix + + + Note: + + + This remediation is designed around the postfix mail server. + Depending on your environment you may have an alternative MTA installed such as sendmail. If this is the case consult the documentation for your installed MTA to configure the recommended state. + + + + + + + + + + + + + Ensure only approved services are listening on a network interface + + A network port is identified by its number, the associated IP address, and the type of the communication protocol such as TCP or UDP. + A listening port is a network port on which an application or process listens on, acting as a communication endpoint. + Each listening port can be open or closed (filtered) using a firewall. In general terms, an open port is a network port that accepts incoming packets from remote locations. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Services listening on the system pose a potential risk as an attack vector. These services should be reviewed, and if not required, the service should be stopped, and the package containing the service should be removed. If required packages have a dependency, the service should be stopped and masked to reduce the attack surface of the system. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + Run the following commands to stop the service and remove the package containing the service: + +# systemctl stop <service_name>.socket <service_name>.service
+# yum remove <package_name> +
+ + -OR- + If required packages have a dependency: + Run the following commands to stop and mask the service and socket: + +# systemctl stop <service_name>.socket <service_name>.service
+# systemctl mask <service_name>.socket <service_name>.service +
+ + Note: + replace <service_name> + with the appropriate service name. + Impact: + + There may be packages that are dependent on the service's package. If the service's package is removed, these dependent packages will be removed as well. Before removing the service's package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask the <service_name>.socket + and <service_name>.service + leaving the service's package installed. + +
+
+
+
+
+ + Configure Service Clients + + A number of insecure services exist. While disabling the servers prevents a local attack against these services, it is advised to remove their clients unless they are required. + + Note: + This should not be considered a comprehensive list of insecure service clients. You may wish to consider additions to those listed here for your environment. + + + Ensure ftp client is not installed + + FTP (File Transfer Protocol) is a traditional and widely used standard tool for transferring files between a server and clients over a network, especially where no authentication is necessary (permits anonymous users to connect to a server). + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + FTP does not protect the confidentiality of data or authentication credentials. It is recommended SFTP be used if file transfer is required. Unless there is a need to run the system as a FTP server (for example, to allow anonymous downloads), it is recommended that the package be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following command to remove ftp +: + # yum remove ftp + + + + + + + + + + + + Ensure ldap client is not installed + + The Lightweight Directory Access Protocol (LDAP) was introduced as a replacement for NIS/YP. It is a service that provides a method for looking up information from a central database. + + + + + + + Devices + Protect + + + + + + Applications + Respond + + + + + + If the system will not need to act as an LDAP client, it is recommended that the software be removed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following command to remove the openldap-clients + package: + # yum remove openldap-clients + + Impact: + + Removing the LDAP client will prevent or inhibit using LDAP for authentication in your environment. + + + + + + + + + + + + Ensure nis client is not installed + + +The Network Information Service (NIS), formerly known as Yellow Pages, is a client-server directory service protocol used to distribute system configuration files. The NIS client ( ypbind + ) was used to bind a machine to an NIS server and receive the distributed configuration files. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + The NIS service is inherently an insecure system that has been vulnerable to DOS attacks, buffer overflows and has poor authentication for querying NIS maps. NIS generally has been replaced by such protocols as Lightweight Directory Access Protocol (LDAP). It is recommended that the service be removed. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + Run the following command to remove the ypbind package: + # yum remove ypbind + + Impact: + + Many insecure service clients are used as troubleshooting tools and in testing environments. Uninstalling them can inhibit capability to test and troubleshoot. If they are required it is advisable to remove the clients after use to prevent accidental or intentional misuse. + + + + + + + + + + + + Ensure telnet client is not installed + + +The telnet + package contains the telnet + client, which allows users to start connections to other systems via the telnet protocol. + + + + + + + Devices + Protect + + + + + + Applications + Respond + + + + + + +The telnet + protocol is insecure and unencrypted. The use of an unencrypted transmission medium could allow an unauthorized user to steal credentials. The ssh + package provides an encrypted session and stronger security and is included in most Linux distributions. + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following command to remove the telnet + package: + # yum remove telnet + + Impact: + + Many insecure service clients are used as troubleshooting tools and in testing environments. Uninstalling them can inhibit capability to test and troubleshoot. If they are required it is advisable to remove the clients after use to prevent accidental or intentional misuse. + + + + + + + + + + + + Ensure tftp client is not installed + + Trivial File Transfer Protocol (TFTP) is a simple protocol for exchanging files between two TCP/IP machines. TFTP servers allow connections from a TFTP Client for sending and receiving files. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + TFTP does not have built-in encryption, access control or authentication. This makes it very easy for an attacker to exploit TFTP to gain access to files + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following command to remove tftp +: + # yum remove tftp + + + + + + + + + + + +
+ + Network + + This section provides guidance on for securing the network configuration of the system + + + Configure Network Devices + + To reduce the attack surface of a system, unused devices should be disabled. + + Note: + This should not be considered a comprehensive list, you may wish to consider additions to those listed here for your environment. + + + Ensure IPv6 status is identified + + Internet Protocol Version 6 (IPv6) is the most recent version of Internet Protocol (IP). It's designed to supply IP addressing and additional security to support the predicted growth of connected devices. IPv6 is based on 128-bit addressing and can support 340 undecillion, which is 340 trillion3 addresses. + Features of IPv6 + + Hierarchical addressing and routing infrastructure + Stateful and Stateless configuration + Support for quality of service (QoS) + An ideal protocol for neighboring node interaction + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + Having more addresses has grown in importance with the expansion of smart devices and connectivity. IPv6 provides more than enough globally unique IP addresses for every networked device currently on the planet, helping ensure providers can keep pace with the expected proliferation of IP-based devices. + + + + IETF RFC 4038 recommends that applications are built with an assumption of dual stack. It is recommended that IPv6 be enabled and configured in accordance with Benchmark recommendations. + + -IF- + dual stack and IPv6 are not used in your environment, IPv6 may be disabled to reduce the attack surface of the system, and recommendations pertaining to IPv6 can be skipped. + + Note: + It is recommended that IPv6 be enabled and configured unless this is against local site policy + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + Enable or disable IPv6 in accordance with system requirements and local site policy + Impact: + + IETF RFC 4038 recommends that applications are built with an assumption of dual stack. + When enabled, IPv6 will require additional configuration to reduce risk to the system. + + + + + + + + + + + + + Ensure wireless interfaces are disabled + + Wireless networking is used when wired networks are unavailable. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + Devices + Protect + + + + + + + -IF- + wireless is not to be used, wireless devices can be disabled to reduce the potential attack surface. + + + + + NIST SP 800-53 Rev. 5: CM-7 + + + + Run the following script to disable any wireless interfaces: + +#!/usr/bin/env bash
+
+{
+ module_fix()
+ {
+ if ! modprobe -n -v "$l_mname" | grep -P -- '^\h*install \/bin\/(true|false)'; then
+ echo -e " - setting module: \"$l_mname\" to be un-loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mname".conf
+ fi
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e " - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ if ! grep -Pq -- "^\h*blacklist\h+$l_mname\b" /etc/modprobe.d/*; then
+ echo -e " - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mname".conf
+ fi
+ }
+ if [ -n "$(find /sys/class/net/*/ -type d -name wireless)" ]; then
+ l_dname=$(for driverdir in $(find /sys/class/net/*/ -type d -name wireless | xargs -0 dirname); do basename "$(readlink -f "$driverdir"/device/driver/module)";done | sort -u)
+ for l_mname in $l_dname; do
+ module_fix
+ done
+ fi
+} +
+ Impact: + + Many if not all laptop workstations and some desktop workstations will connect via wireless requiring these interfaces be enabled. + +
+
+
+ + + + + + +
+ + Ensure bluetooth services are not in use + + Bluetooth is a short-range wireless technology standard that is used for exchanging data between devices over short distances. It employs UHF radio waves in the ISM bands, from 2.402 GHz to 2.48 GHz. It is mainly used as an alternative to wire connections. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +An attacker may be able to find a way to access or corrupt your data. One example of this type of activity is bluesnarfing +, which refers to attackers using a Bluetooth connection to steal information off of your Bluetooth device. Also, viruses or other malicious code can take advantage of Bluetooth technology to infect other devices. If you are infected, your data may be corrupted, compromised, stolen, or lost. + + + + https://www.cisa.gov/tips/st05-015 + NIST SP 800-53 Rev. 5: CM-7 + + + + +Run the following commands to stop bluetooth.service +, and remove the bluez + package: + +# systemctl stop bluetooth.service
+# yum remove bluez +
+ + -OR- + + + -IF- + the bluez + package is required as a dependency: + +Run the following commands to stop and mask bluetooth.service +: + +# systemctl stop bluetooth.service
+# systemctl mask bluetooth.service +
+ + Note: + A reboot may be required + Impact: + + Many personal electronic devices (PEDs) use Bluetooth technology. For example, you may be able to operate your computer with a wireless keyboard. Disabling Bluetooth will prevent these devices from connecting to the system. + +There may be packages that are dependent on the bluez + package. If the bluez + package is removed, these dependent packages will be removed as well. Before removing the bluez + package, review any dependent packages to determine if they are required on the system. + + -IF- + a dependent package is required: stop and mask bluetooth.service + leaving the bluez + package installed. + +
+
+
+ + + + + + + + + + + + + + + +
+
+ + Configure Network Kernel Modules + + The Linux kernel modules support several network protocols that are not commonly used. If these protocols are not needed, it is recommended that they be disabled in the kernel. + + Note: + This should not be considered a comprehensive list of uncommon network protocols, you may wish to consider additions to those listed here for your environment. + + + Ensure dccp kernel module is not available + + The Datagram Congestion Control Protocol (DCCP) is a transport layer protocol that supports streaming media and telephony. DCCP provides a way to gain access to congestion control, without having to do it at the application layer, but does not provide in-sequence delivery. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + + -IF- + the protocol is not required, it is recommended that the drivers not be installed to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: SI-4, CM-7 + + + + +Run the following script to disable the dccp + module: + + -IF- + the module is available in the running kernel: + + +Create a file with install dccp /bin/false + in the /etc/modprobe.d/ + directory + +Create a file with blacklist dccp + in the /etc/modprobe.d/ + directory + +Unload dccp + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file with blacklist dccp + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="dccp" # set module name
+ l_mtype="net" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure tipc kernel module is not available + + The Transparent Inter-Process Communication (TIPC) protocol is designed to provide communication between cluster nodes. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + + -IF- + the protocol is not being used, it is recommended that kernel module not be loaded, disabling the service to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: SI-4, CM-7 + + + + +Run the following script to disable the tipc + module: + + -IF- + the module is available in the running kernel: + + +Create a file with install tipc /bin/false + in the /etc/modprobe.d/ + directory + +Create a file with blacklist tipc + in the /etc/modprobe.d/ + directory + +Unload tipc + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file with blacklist tipc + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="tipc" # set module name
+ l_mtype="net" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure rds kernel module is not available + + The Reliable Datagram Sockets (RDS) protocol is a transport layer protocol designed to provide low-latency, high-bandwidth communications between cluster nodes. It was developed by the Oracle Corporation. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + + -IF- + the protocol is not being used, it is recommended that kernel module not be loaded, disabling the service to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: SI-4, CM-7 + + + + +Run the following script to disable the rds + module: + + -IF- + the module is available in the running kernel: + + +Create a file with install rds /bin/false + in the /etc/modprobe.d/ + directory + +Create a file with blacklist rds + in the /etc/modprobe.d/ + directory + +Unload rds + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file with blacklist rds + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="rds" # set module name
+ l_mtype="net" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+
+
+
+ + + + + + + +
+ + Ensure sctp kernel module is not available + + The Stream Control Transmission Protocol (SCTP) is a transport layer protocol used to support message oriented communication, with several streams of messages in one connection. It serves a similar function as TCP and UDP, incorporating features of both. It is message-oriented like UDP, and ensures reliable in-sequence transport of messages with congestion control like TCP. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + + -IF- + the protocol is not being used, it is recommended that kernel module not be loaded, disabling the service to reduce the potential attack surface. + + + + NIST SP 800-53 Rev. 5: SI-4, CM-7 + + + + +Run the following script to disable the sctp + module: + + -IF- + the module is available in the running kernel: + + +Create a file with install sctp /bin/false + in the /etc/modprobe.d/ + directory + +Create a file with blacklist sctp + in the /etc/modprobe.d/ + directory + +Unload sctp + from the kernel + + + -IF- + available in ANY installed kernel: + + +Create a file with blacklist sctp + in the /etc/modprobe.d/ + directory + + + -IF- + the kernel module is not available on the system or pre-compiled into the kernel: + + No remediation is necessary + + +#!/usr/bin/env bash
+
+{
+ l_mname="sctp" # set module name
+ l_mtype="net" # set module type
+ l_mpath="/lib/modules/**/kernel/$l_mtype"
+ l_mpname="$(tr '-' '_' <<< "$l_mname")"
+ l_mndir="$(tr '-' '/' <<< "$l_mname")"
+
+ module_loadable_fix()
+ {
+ # If the module is currently loadable, add "install {MODULE_NAME} /bin/false" to a file in "/etc/modprobe.d"
+ l_loadable="$(modprobe -n -v "$l_mname")"
+ [ "$(wc -l <<< "$l_loadable")" -gt "1" ] && l_loadable="$(grep -P -- "(^\h*install|\b$l_mname)\b" <<< "$l_loadable")"
+ if ! grep -Pq -- '^\h*install \/bin\/(true|false)' <<< "$l_loadable"; then
+ echo -e "\n - setting module: \"$l_mname\" to be not loadable"
+ echo -e "install $l_mname /bin/false" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ module_loaded_fix()
+ {
+ # If the module is currently loaded, unload the module
+ if lsmod | grep "$l_mname" > /dev/null 2>&1; then
+ echo -e "\n - unloading module \"$l_mname\""
+ modprobe -r "$l_mname"
+ fi
+ }
+ module_deny_fix()
+ {
+ # If the module isn't deny listed, denylist the module
+ if ! modprobe --showconfig | grep -Pq -- "^\h*blacklist\h+$l_mpname\b"; then
+ echo -e "\n - deny listing \"$l_mname\""
+ echo -e "blacklist $l_mname" >> /etc/modprobe.d/"$l_mpname".conf
+ fi
+ }
+ # Check if the module exists on the system
+ for l_mdir in $l_mpath; do
+ if [ -d "$l_mdir/$l_mndir" ] && [ -n "$(ls -A $l_mdir/$l_mndir)" ]; then
+ echo -e "\n - module: \"$l_mname\" exists in \"$l_mdir\"\n - checking if disabled..."
+ module_deny_fix
+ if [ "$l_mdir" = "/lib/modules/$(uname -r)/kernel/$l_mtype" ]; then
+ module_loadable_fix
+ module_loaded_fix
+ fi
+ else
+ echo -e "\n - module: \"$l_mname\" doesn't exist in \"$l_mdir\"\n"
+ fi
+ done
+ echo -e "\n - remediation of module: \"$l_mname\" complete\n"
+} +
+
+
+
+ + + + + + + +
+
+ + Configure Network Kernel Parameters + + The following network parameters are intended for use on both host only and router systems. A system acts as a router if it has at least two interfaces and is configured to perform routing functions. + + Note: + + + +sysctl settings are defined through files in /usr/local/lib +, /usr/lib/ +, /lib/ +, /run/ +, and /etc/ + + +Files are typically placed in the sysctl.d + directory within the parent directory + +The paths where sysctl preload files usually exist + + + /run/sysctl.d/*.conf + + + /etc/sysctl.d/*.conf + + + /usr/local/lib/sysctl.d/*.conf + + + /usr/lib/sysctl.d/*.conf + + + /lib/sysctl.d/*.conf + + + /etc/sysctl.conf + + + + +Files must have the " .conf +" extension + +Vendors settings usually live in /usr/lib/ + or /usr/local/lib/ + + +To override a whole file, create a new file with the same name in /etc/sysctl.d/ + and put new settings there. + +To override only specific settings, add a file with a lexically later name in /etc/sysctl.d/ + and put new settings there. + + Entries listed latter in the file take precedence over the same settings listed earlier in the file + Files containing kernel parameters that are over-ridden by other files with the same name will not be listed + On systems running UncomplicatedFirewall, the kernel parameters may be set or over-written. This will not be visible in the output of the command + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + +The settings in /etc/ufw/sysctl.conf + will override settings other settings + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + + Ensure ip forwarding is disabled + + +The net.ipv4.ip_forward + and net.ipv6.conf.all.forwarding + flags are used to tell the system whether it can forward packets or not. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +Setting net.ipv4.ip_forward + and net.ipv6.conf.all.forwarding + to 0 + ensures that a system with multiple interfaces (for example, a hard proxy), will never be able to forward packets, and therefore, never serve as a router. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.ip_forward = 0 + + + + Example: + + # printf '%s\n' "net.ipv4.ip_forward = 0" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv4.ip_forward=0
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + -IF- + IPv6 is enabled on the system: + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv6.conf.all.forwarding = 0 + + + + Example: + + # printf '%s\n' "net.ipv6.conf.all.forwarding = 0" >> /etc/sysctl.d/60-netipv6_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv6.conf.all.forwarding=0
+ sysctl -w net.ipv6.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten + Impact: + + IP forwarding is required on systems configured to act as a router. If these parameters are disabled, the system will not be able to perform as a router. + Many Cloud Service Provider (CSP) hosted systems require IP forwarding to be enabled. If the system is running on a CSP platform, this requirement should be reviewed before disabling IP forwarding. + +
+
+
+ + + + + + + + + + + + +
+ + Ensure packet redirect sending is disabled + + ICMP Redirects are used to send routing information to other hosts. As a host itself does not act as a router (in a host only configuration), there is no need to send redirects. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + An attacker could use a compromised host to send invalid ICMP redirects to other router devices in an attempt to corrupt routing and have users access a system set up by the attacker as opposed to a valid system. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.send_redirects = 0 + + + net.ipv4.conf.default.send_redirects = 0 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.send_redirects = 0" "net.ipv4.conf.default.send_redirects = 0" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv4.conf.all.send_redirects=0
+ sysctl -w net.ipv4.conf.default.send_redirects=0
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten + Impact: + + IP forwarding is required on systems configured to act as a router. If these parameters are disabled, the system will not be able to perform as a router. + +
+
+
+ + + + + + + + + + + + +
+ + Ensure bogus icmp responses are ignored + + +Setting net.ipv4.icmp_ignore_bogus_error_responses + to 1 + prevents the kernel from logging bogus responses (RFC-1122 non-compliant) from broadcast reframes, keeping file systems from filling up with useless log messages. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + Some routers (and some attackers) will send responses that violate RFC-1122 and attempt to fill up a log file system with many useless error messages. + + + + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.icmp_ignore_bogus_error_responses = 1 + + + + Example: + + # printf '%s\n' "net.ipv4.icmp_ignore_bogus_error_responses = 1" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv4.icmp_ignore_bogus_error_responses=1
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + +
+ + Ensure broadcast icmp requests are ignored + + +Setting net.ipv4.icmp_echo_ignore_broadcasts + to 1 + will cause the system to ignore all ICMP echo and timestamp requests to broadcast and multicast addresses. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + Accepting ICMP echo and timestamp requests with broadcast or multicast destinations for your network could be used to trick your host into starting (or participating) in a Smurf attack. A Smurf attack relies on an attacker sending large amounts of ICMP broadcast messages with a spoofed source address. All hosts receiving this message and responding would send echo-reply messages back to the spoofed address, which is probably not routable. If many hosts respond to the packets, the amount of traffic on the network could be significantly multiplied. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.icmp_echo_ignore_broadcasts = 1 + + + + Example: + + # printf '%s\n' "net.ipv4.icmp_echo_ignore_broadcasts = 1" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + +
+ + Ensure icmp redirects are not accepted + + ICMP redirect messages are packets that convey routing information and tell your host (acting as a router) to send packets via an alternate path. It is a way of allowing an outside routing device to update your system routing tables. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +ICMP redirect messages are packets that convey routing information and tell your host (acting as a router) to send packets via an alternate path. It is a way of allowing an outside routing device to update your system routing tables. By setting net.ipv4.conf.all.accept_redirects +, net.ipv4.conf.default.accept_redirects +, net.ipv6.conf.all.accept_redirects +, and net.ipv6.conf.default.accept_redirects + to 0 +, the system will not accept any ICMP redirect messages, and therefore, won't allow outsiders to update the system's routing tables. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.accept_redirects = 0 + + + net.ipv4.conf.default.accept_redirects = 0 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.accept_redirects = 0" "net.ipv4.conf.default.accept_redirects = 0" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv4.conf.all.accept_redirects=0
+ sysctl -w net.ipv4.conf.default.accept_redirects=0
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + -IF- + IPv6 is enabled on the system: + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv6.conf.all.accept_redirects = 0 + + + net.ipv6.conf.default.accept_redirects = 0 + + + + Example: + + # printf '%s\n' "net.ipv6.conf.all.accept_redirects = 0" "net.ipv6.conf.default.accept_redirects = 0" >> /etc/sysctl.d/60-netipv6_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv6.conf.all.accept_redirects=0
+ sysctl -w net.ipv6.conf.default.accept_redirects=0
+ sysctl -w net.ipv6.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure secure icmp redirects are not accepted + + Secure ICMP redirects are the same as ICMP redirects, except they come from gateways listed on the default gateway list. It is assumed that these gateways are known to your system, and that they are likely to be secure. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +It is still possible for even known gateways to be compromised. Setting net.ipv4.conf.all.secure_redirects + and net.ipv4.conf.default.secure_redirects + to 0 + protects the system from routing table updates by possibly compromised known gateways. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.secure_redirects = 0 + + + net.ipv4.conf.default.secure_redirects = 0 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.secure_redirects = 0" "net.ipv4.conf.default.secure_redirects = 0" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following commands to set the active kernel parameters: + +# {
+ sysctl -w net.ipv4.conf.all.secure_redirects=0
+ sysctl -w net.ipv4.conf.default.secure_redirects=0
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + + + + + + +
+ + Ensure reverse path filtering is enabled + + +Setting net.ipv4.conf.all.rp_filter + and net.ipv4.conf.default.rp_filter + to 1 + forces the Linux kernel to utilize reverse path filtering on a received packet to determine if the packet was valid. Essentially, with reverse path filtering, if the return packet does not go out the same interface that the corresponding source packet came from, the packet is dropped (and logged if log_martians + is set). + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +Setting net.ipv4.conf.all.rp_filter + and net.ipv4.conf.default.rp_filter + to 1 + is a good way to deter attackers from sending your system bogus packets that cannot be responded to. One instance where this feature breaks down is if asymmetrical routing is employed. This would occur when using dynamic routing protocols (bgp, ospf, etc) on your system. If you are using asymmetrical routing on your system, you will not be able to enable this feature without breaking the routing. + + + + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.rp_filter = 1 + + + net.ipv4.conf.default.rp_filter = 1 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.rp_filter = 1" "net.ipv4.conf.default.rp_filter = 1" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following commands to set the active kernel parameters: + +# {
+ sysctl -w net.ipv4.conf.all.rp_filter=1
+ sysctl -w net.ipv4.conf.default.rp_filter=1
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten + Impact: + + If you are using asymmetrical routing on your system, you will not be able to enable this feature without breaking the routing. + +
+
+
+ + + + + + + + + + + + +
+ + Ensure source routed packets are not accepted + + In networking, source routing allows a sender to partially or fully specify the route packets take through a network. In contrast, non-source routed packets travel a path determined by routers in the network. In some cases, systems may not be routable or reachable from some locations (e.g. private addresses vs. Internet routable), and so source routed packets would need to be used. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +Setting net.ipv4.conf.all.accept_source_route +, net.ipv4.conf.default.accept_source_route +, net.ipv6.conf.all.accept_source_route + and net.ipv6.conf.default.accept_source_route + to 0 + disables the system from accepting source routed packets. Assume this system was capable of routing packets to Internet routable addresses on one interface and private addresses on another interface. Assume that the private addresses were not routable to the Internet routable addresses and vice versa. Under normal routing circumstances, an attacker from the Internet routable addresses could not use the system as a way to reach the private address systems. If, however, source routed packets were allowed, they could be used to gain access to the private address systems as the route could be specified, rather than rely on routing protocols that did not allow this routing. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.accept_source_route = 0 + + + net.ipv4.conf.default.accept_source_route = 0 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.accept_source_route = 0" "net.ipv4.conf.default.accept_source_route = 0" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv4.conf.all.accept_source_route=0
+ sysctl -w net.ipv4.conf.default.accept_source_route=0
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + -IF- + IPv6 is enabled on the system: + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv6.conf.all.accept_source_route = 0 + + + net.ipv6.conf.default.accept_source_route = 0 + + + + Example: + + # printf '%s\n' "net.ipv6.conf.all.accept_source_route = 0" "net.ipv6.conf.default.accept_source_route = 0" >> /etc/sysctl.d/60-netipv6_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv6.conf.all.accept_source_route=0
+ sysctl -w net.ipv6.conf.default.accept_source_route=0
+ sysctl -w net.ipv6.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure suspicious packets are logged + + When enabled, this feature logs packets with un-routable source addresses to the kernel log. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +Setting net.ipv4.conf.all.log_martians + and net.ipv4.conf.default.log_martians to +1` enables this feature. Logging these packets allows an administrator to investigate the possibility that an attacker is sending spoofed packets to their system. + + + + + NIST SP 800-53 Rev. 5: AU-3 + + + + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.conf.all.log_martians = 1 + + + net.ipv4.conf.default.log_martians = 1 + + + + Example: + + # printf '%s\n' "net.ipv4.conf.all.log_martians = 1" "net.ipv4.conf.default.log_martians = 1" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv4.conf.all.log_martians=1
+ sysctl -w net.ipv4.conf.default.log_martians=1
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + + + + + + +
+ + Ensure tcp syn cookies is enabled + + +When tcp_syncookies + is set, the kernel will handle TCP SYN packets normally until the half-open connection queue is full, at which time, the SYN cookie functionality kicks in. SYN cookies work by not using the SYN queue at all. Instead, the kernel simply replies to the SYN with a SYN|ACK, but will include a specially crafted TCP sequence number that encodes the source and destination IP address and port number and the time the packet was sent. A legitimate connection would send the ACK packet of the three way handshake with the specially crafted sequence number. This allows the system to verify that it has received a valid response to a SYN cookie and allow the connection, even though there is no corresponding SYN in the queue. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +Attackers use SYN flood attacks to perform a denial of service attacked on a system by sending many SYN packets without completing the three way handshake. This will quickly use up slots in the kernel's half-open connection queue and prevent legitimate connections from succeeding. Setting net.ipv4.tcp_syncookies + to 1 + enables SYN cookies, allowing the system to keep accepting valid connections, even if under a denial of service attack. + + + + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Set the following parameter in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv4.tcp_syncookies = 1 + + + + Example: + + # printf '%s\n' "net.ipv4.tcp_syncookies = 1" >> /etc/sysctl.d/60-netipv4_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv4.tcp_syncookies=1
+ sysctl -w net.ipv4.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + +
+ + Ensure ipv6 router advertisements are not accepted + + This setting disables the system's ability to accept IPv6 router advertisements. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +On systems with Uncomplicated Firewall, additional settings may be configured in /etc/ufw/sysctl.conf + + + +The settings in /etc/ufw/sysctl.conf + will override settings in /etc/sysctl.conf + + +This behavior can be changed by updating the IPT_SYSCTL + parameter in /etc/default/ufw + + + + + + +It is recommended that systems do not accept router advertisements as they could be tricked into routing traffic to compromised machines. Setting hard routes within the system (usually a single default route to a trusted router) protects the system from bad routes. Setting net.ipv6.conf.all.accept_ra + and net.ipv6.conf.default.accept_ra + to 0 + disables the system's ability to accept IPv6 router advertisements. + + + + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + + -IF- + IPv6 is enabled on the system: + +Set the following parameters in /etc/sysctl.conf + or a file in /etc/sysctl.d/ + ending in .conf +: + + + net.ipv6.conf.all.accept_ra = 0 + + + net.ipv6.conf.default.accept_ra = 0 + + + + Example: + + # printf '%s\n' "net.ipv6.conf.all.accept_ra = 0" "net.ipv6.conf.default.accept_ra = 0" >> /etc/sysctl.d/60-netipv6_sysctl.conf + + Run the following command to set the active kernel parameters: + +# {
+ sysctl -w net.ipv6.conf.all.accept_ra=0
+ sysctl -w net.ipv6.conf.default.accept_ra=0
+ sysctl -w net.ipv6.route.flush=1
+} +
+ + Note: + If these settings appear in a canonically later file, or later in the same file, these settings will be overwritten +
+
+
+ + + + + + + + + + + + +
+
+ + Configure Host Based Firewall + + A firewall is a set of rules. When a data packet moves into or out of a protected network space, its contents (in particular, information about its origin, target, and the protocol it plans to use) are tested against the firewall rules to see if it should be allowed through + To provide a Host Based Firewall, the Linux kernel includes support for: + + Netfilter - A set of hooks inside the Linux kernel that allows kernel modules to register callback functions with the network stack. A registered callback function is then called back for every packet that traverses the respective hook within the network stack. Includes the ip_tables, ip6_tables, arp_tables, and ebtables kernel modules. These modules are some of the significant parts of the Netfilter hook system. + +nftables - A subsystem of the Linux kernel providing filtering and classification of network packets/datagrams/frames. nftables is supposed to replace certain parts of Netfilter, while keeping and reusing most of it. nftables utilizes the building blocks of the Netfilter infrastructure, such as the existing hooks into the networking stack, connection tracking system, userspace queueing component, and logging subsystem. Is available in Linux kernels 3.13 and newer +. + + In order to configure firewall rules for Netfilter or nftables, a firewall utility needs to be installed. Guidance has been included for the following firewall utilities: + + FirewallD - Provides firewall features by acting as a front-end for the Linux kernel's netfilter framework via the iptables backend. Starting in v0.6.0, FirewallD added support for acting as a front-end for the Linux kernel's netfilter framework via the nftables userspace utility, acting as an alternative to the nft command line program. firewalld supports both IPv4 and IPv6 networks and can administer separate firewall zones with varying degrees of trust as defined in zone profiles. + nftables - Includes the nft utility for configuration of the nftables subsystem of the Linux kernel + iptables - Includes the iptables, ip6tables, arptables and ebtables utilities for configuration Netfilter and the ip_tables, ip6_tables, arp_tables, and ebtables kernel modules. + + + Note: + + + +Only one + method should be used to configure a firewall on the system. Use of more than one method could produce unexpected results. + This section is intended only to ensure the resulting firewall rules are in place, not how they are configured. + + + + Configure firewall utility + + In order to configure firewall rules for Netfilter or nftables, a firewall utility needs to be installed. Guidance has been included for the following firewall utilities: + + + FirewallD: + + Provides firewall features by acting as a front-end for the Linux kernel's netfilter framework via the nftables userspace utility, acting as an alternative to the nft command line program. firewalld supports both IPv4 and IPv6 networks and can administer separate firewall zones with varying degrees of trust as defined in zone profiles. + Use the firewalld utility for simple firewall use cases. The utility is easy to use and covers the typical use cases for these scenarios. + + + + NFTables: + + Includes the nft utility for configuration of the nftables subsystem of the Linux kernel + Use the nftables utility to set up complex and performance critical firewalls, such as for a whole network. + + + + IPTables services: + + The iptables-services package contains the iptables service and the ip6tables service. + +With the iptables service, every single change means flushing all the old rules and reading all the new rules from /etc/sysconfig/iptables + + + + + + CAUTION: + Only one + method should be used to configure a firewall on the system. Use of more than one method could produce unexpected results. + + + Ensure iptables is installed + + IPtables is an application that allows a system administrator to configure the IPv4 and IPv6 tables, chains and rules provided by the Linux kernel firewall + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + IPTables is a subsystem of the Linux kernel that can protect against threats originating from within a corporate network to include malicious mobile code and poorly configured software on a host. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + +Run the following command to install nftables + + # yum install nftables + + Impact: + + Changing firewall settings while connected over the network can result in being locked out of the system. + + + + + + + + + + + + Ensure a single firewall configuration utility is in use + + FirewallD - Is a firewall service daemon that provides a dynamic customizable host-based firewall with a D-Bus interface. Being dynamic, it enables creating, changing, and deleting the rules without the necessity to restart the firewall daemon each time the rules are changed + NFTables - Includes the nft utility for configuration of the nftables subsystem of the Linux kernel + +IPTables Services - Contains the iptables service and the ip6tables service which store their configurations in /etc/sysconfig/iptables + and /etc/sysconfig/ip6tables + + + Note: + firewalld with nftables backend does not support passing custom nftables rules to firewalld, using the --direct + option. + + + + + + + Devices + Protect + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + In order to configure firewall rules, a firewall utility needs to be installed and active of the system. The use of more than one firewall utility may produce unexpected results. + + + + + + + + Determine which firewall utility to use in your environment. Ensure it also follows local site policy, and follow the guidance for that option: + + OPTION 1 + - FirewallD: + +Run the following command to uninstall nftables + and iptables-services + + # yum remove nftables iptables-services + + + - OR - + If the package is required for a dependency and is authorized by local sight policy, run the following commands to stop and mask nftables.service +, iptables.service +, and ip6tables.service +: + +# systemctl stop nftables.service iptables.service ip6tables.service
+# systemctl mask nftables.service iptables.service ip6tables.service +
+ Follow the guidance in subsection "Configure firewalld. Skip sections "Configure nftables" and "Configure iptables" + + OPTION 2 + - NFTables: + +Run the following command to uninstall firewalld + and iptables-services + + # yum remove firewalld iptables-services + + + - OR - + If the package is required for a dependency and is authorized by local sight policy, run the following commands to stop and mask firewalld.service +, iptables.service +, and ip6tables.service +: + +# systemctl stop firewalld.service iptables.service ip6tables.service
+# systemctl mask firewalld.service iptables.service ip6tables.service +
+ Follow the guidance in subsection "Configure nftables". Skip sections "Configure firewalld" and "Configure iptables" + + OPTION 3 + - IPTables: + +Run the following command to uninstall nftables + and iptables-services + + # yum remove firewalld nftables + + + - OR - + If the package is required for a dependency and is authorized by local sight policy, run the following commands to stop and mask firewalld.service + and nftables.service +: + +# systemctl stop firewalld.service nftables.service
+# systemctl mask firewalld.service nftables.service +
+ Follow the guidance in subsection "Configure iptables" skip sections "Configure firewalld" and "Configure nftables" +
+
+
+ + + + + + +
+
+ + Configure firewalld + + + CAUTION: - IF - nftables or iptables are being used in your environment, please follow the guidance in their respective section and pass-over the guidance in this section. + + firewalld (Dynamic Firewall Manager) provides a dynamically managed firewall with support for network/firewall “zones” to assign a level of trust to a network and its associated connections, interfaces or sources. It has support for IPv4, IPv6, Ethernet bridges and also for IPSet firewall settings. There is a separation of the runtime and permanent configuration options. It also provides an interface for services or applications to add iptables, ip6tables and ebtables rules directly. This interface can also be used by advanced users. + In the v0.6.0 release, firewalld gained support for using nftables as a firewall back-end. + +The following example will create a FirewallD Zone called securezone + to implement the firewall rules of this section leveraging the firewalld utility included with the firewalld package. This example will open port 22(ssh) from anywhere. Opening service SSH + should be updated in accordance with local site policy. If another name for the zone is preferred, replace securezone + with the name to be used. + + Sample FirewallD securezone zone xml file + + +<?xml version="1.0" encoding="utf-8"?>
+<zone target="DROP">
+ <short>securezone</short>
+ <description>For use with CIS Linux Benchmark. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
+ <service name="ssh"/>
+ <service name="dhcpv6-client"/>
+ <icmp-block name="destination-unreachable"/>
+ <icmp-block name="packet-too-big"/>
+ <icmp-block name="time-exceeded"/>
+ <icmp-block name="parameter-problem"/>
+ <icmp-block name="neighbour-advertisement"/>
+ <icmp-block name="neighbour-solicitation"/>
+ <icmp-block name="router-advertisement"/>
+ <icmp-block name="router-solicitation"/>
+ <rule family="ipv4">
+ <source address="127.0.0.1"/>
+ <destination address="127.0.0.1" invert="True"/>
+ <drop/>
+ </rule>
+ <rule family="ipv6">
+ <source address="::1"/>
+ <destination address="::1" invert="True"/>
+ <drop/>
+ </rule>
+ <icmp-block-inversion/>
+</zone> +
+ +To use this zone, save this as /etc/firewalld/zones/securezone.xml + and run the following commands: + +# firewall-cmd --reload
+# firewall-cmd --permanent --zone=securezone --change-interface={NAME OF NETWORK INTERFACE} +
+ To make this zone the default zone, runt the following command: + # firewall-cmd --set-default-zone=securezone + + + Note: + Configuration of a live system's firewall directly over a remote connection will often result in being locked out. +
+ + Ensure firewalld is installed + + firewalld is a firewall management tool for Linux operating systems. It provides firewall features by acting as a front-end for the Linux kernel's netfilter framework via the iptables backend or provides firewall features by acting as a front-end for the Linux kernel's netfilter framework via the nftables utility. + firewalld replaces iptables as the default firewall management tool. Use the firewalld utility to configure a firewall for less complex firewalls. The utility is easy to use and covers the typical use cases scenario. FirewallD supports both IPv4 and IPv6 networks and can administer separate firewall zones with varying degrees of trust as defined in zone profiles. + + Note: + Starting in v0.6.0, FirewallD added support for acting as a front-end for the Linux kernel's netfilter framework via the nftables userspace utility, acting as an alternative to the nft command line program. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + A firewall utility is required to configure the Linux kernel's netfilter framework via the iptables or nftables back-end. + The Linux kernel's netfilter framework host-based firewall can protect against threats originating from within a corporate network to include malicious mobile code and poorly configured software on a host. + + Note: + Only one + firewall utility should be installed and configured. FirewallD + is dependent on the iptables + package. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + +Run the following command to install FirewallD + and iptables +: + # yum install firewalld iptables + + Impact: + + Changing firewall settings while connected over the network can result in being locked out of the system. + + + + + + + + + + + + + + + + + + + + + Ensure firewalld service enabled and running + + + firewalld.service + enables the enforcement of firewall rules configured through firewalld + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +Ensure that the firewalld.service + is enabled and running to enforce firewall rules configured through firewalld + + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + +Run the following command to unmask firewalld + + # systemctl unmask firewalld + + +Run the following command to enable and start firewalld + + # systemctl --now enable firewalld + + Impact: + + Changing firewall settings while connected over network can result in being locked out of the system. + + + + + + + + + + + + + + + + + + + + + + + + Ensure firewalld drops unnecessary services and ports + + Services and ports can be accepted or explicitly rejected or dropped by a zone. + For every zone, you can set a default behavior that handles incoming traffic that is not further specified. Such behavior is defined by setting the target of the zone. There are three options - default, ACCEPT, REJECT, and DROP. + + ACCEPT - you accept all incoming packets except those disabled by a specific rule. + REJECT - you disable all incoming packets except those that you have allowed in specific rules and the source machine is informed about the rejection. + DROP - you disable all incoming packets except those that you have allowed in specific rules and no information sent to the source machine. + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + To reduce the attack surface of a system, all services and ports should be blocked unless required + + + + firewalld.service(5) + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/securing_networks/using-and-configuring-firewalls_securing-networks + NIST SP 800-53 Rev. 5: CA-9 + + + + If Firewalld is in use on the system: + Run the following command to remove an unnecessary service: + # firewall-cmd --remove-service=<service> + + + Example: + + # firewall-cmd --remove-service=cockpit + + Run the following command to remove an unnecessary port: + # firewall-cmd --remove-port=<port-number>/<port-type> + + + Example: + + # firewall-cmd --remove-port=25/tcp + + Run the following command to make new settings persistent: + # firewall-cmd --runtime-to-permanent + + + + + + + Ensure network interfaces are assigned to appropriate zone + + firewall zones define the trust level of network connections or interfaces. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + +The firewall in the Linux kernel is not able to handle network connections with the name shown by NetworkManager, it can only handle the network interfaces used by the connection. Because of this NetworkManager tells firewalld to assign the network interface that is used for this connection to the zone defined in the configuration of that connection. This assignment happens before the interface is used. The configuration of the connection can either be the NetworkManager configuration or also an ifcfg +. + + Example: If the zone is not set in the configuration file, the interfaces will be assigned to the default zone defined in the firewalld configuration. If a connection has more than one interface, all of them will be supplied to firewalld. Also changes in the names of interfaces will be handled by NetworkManager and supplied to firewalld. + + If the zone is not set in the configuration file, the interfaces will be assigned to the default zone defined in the firewalld configuration + + + + A network interface not assigned to the appropriate zone can allow unexpected or undesired network traffic to be accepted on the interface. + + + + https://firewalld.org/documentation/zone/connections-interfaces-and-sources.html + NIST SP 800-53 Rev. 5: CA-9, SC-7 + + + + Run the following command to assign an interface to the approprate zone. + # firewall-cmd --zone=<Zone NAME> --change-interface=<INTERFACE NAME> + + + Example: + + # firewall-cmd --zone=customezone --change-interface=eth0 + + Impact: + + Changing firewall settings while connected over network can result in being locked out of the system. + + + + + +
+ + Configure nftables + + + + If firewalld or iptables are being used in your environment, please follow the guidance in their respective section and pass-over the guidance in this section. + + + +nftables is a subsystem of the Linux kernel providing filtering and classification of network packets/datagrams/frames and is the successor to iptables. The biggest change with the successor nftables is its simplicity. With iptables, we have to configure every single rule and use the syntax which can be compared with normal commands. With nftables, the simpler syntax, much like BPF (Berkely Packet Filter) means shorter lines and less repetition. Support for nftables should also be compiled into the kernel, together with the related nftables modules. It is available in Linux kernels >= 3.13. Please ensure that your kernel supports nftables before choosing this option. + + +This section broadly assumes starting with an empty nftables firewall ruleset (established by flushing the rules with nft flush ruleset). Remediation steps included only affect the live system, you will also need to configure your default firewall configuration to apply on boot. Configuration of a live systems firewall directly over a remote connection will often result in being locked out +. It is advised to have a known good firewall configuration set to run on boot and to configure an entire firewall structure in a script that is then run and tested before saving to boot. + + + Note +: Configuration of a live systems firewall directly over a remote connection will often result in being locked out. It is advised to have a known good firewall configuration set to run on boot and to configure an entire firewall structure in a script that is then run and tested before saving to boot. + + + The following will implement the firewall rules of this section and open ICMP, IGMP, and port 22(ssh) from anywhere. Opening the ports for ICMP, IGMP, and port 22(ssh) needs to be updated in accordance with local site policy. Allow port 22(ssh) needs to be updated to only allow systems requiring ssh connectivity to connect, as per site policy. + + +Save the script bellow as /etc/nftables/nftables.rules + + +#!/sbin/nft -f
+
+# This nftables.rules config should be saved as /etc/nftables/nftables.rules
+
+# flush nftables rulesset
+flush ruleset
+
+# Load nftables ruleset
+
+# nftables config with inet table named filter
+
+table inet filter {
+ # Base chain for input hook named input (Filters inbound network packets)
+ chain input {
+ type filter hook input priority 0; policy drop;
+
+ # Ensure loopback traffic is configured
+ iif "lo" accept
+ ip saddr 127.0.0.0/8 counter packets 0 bytes 0 drop
+ ip6 saddr ::1 counter packets 0 bytes 0 drop
+
+ # Ensure established connections are configured
+ ip protocol tcp ct state established accept
+ ip protocol udp ct state established accept
+ ip protocol icmp ct state established accept
+
+ # Accept port 22(SSH) traffic from anywhere
+ tcp dport ssh accept
+
+ # Accept ICMP and IGMP from anywhere
+ icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-done, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept
+ icmp type { destination-unreachable, router-advertisement, router-solicitation, time-exceeded, parameter-problem } accept
+ ip protocol igmp accept
+ }
+
+ # Base chain for hook forward named forward (Filters forwarded network packets)
+ chain forward {
+ type filter hook forward priority 0; policy drop;
+ }
+
+ # Base chain for hook output named output (Filters outbount network packets)
+ chain output {
+ type filter hook output priority 0; policy drop;
+ # Ensure outbound and established connections are configured
+ ip protocol tcp ct state established,related,new accept
+ ip protocol udp ct state established,related,new accept
+ ip protocol icmp ct state established,related,new accept
+ }
+} +
+ Run the following command to load the file into nftables + # nft -f /etc/nftables/nftables.rules + + + All changes in the nftables subsections are temporary + + To make these changes permanent: + Run the following command to create the nftables.rules file + nft list ruleset > /etc/nftables/nftables.rules + + +Add the following line to /etc/sysconfig/nftables.conf + + include "/etc/nftables/nftables.rules" + +
+ + Ensure nftables is installed + + nftables provides a new in-kernel packet classification framework that is based on a network-specific Virtual Machine (VM) and a new nft userspace command line tool. nftables reuses the existing Netfilter subsystems such as the existing hook infrastructure, the connection tracking system, NAT, userspace queuing and logging subsystem. + + Note: + + + + nftables is available in Linux kernel 3.13 and newer. + + + +Only one + firewall utility should be installed and configured. + + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + nftables is a subsystem of the Linux kernel that can protect against threats originating from within a corporate network to include malicious mobile code and poorly configured software on a host. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + +Run the following command to install nftables + + # yum install nftables + + Impact: + + Changing firewall settings while connected over the network can result in being locked out of the system. + + + + + + + + + + + + + + + + Ensure iptables are flushed with nftables + + nftables is a replacement for iptables, ip6tables, ebtables and arptables + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + It is possible to mix iptables and nftables. However, this increases complexity and also the chance to introduce errors. For simplicity flush out all iptables rules, and ensure it is not loaded + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Run the following commands to flush iptables: + For iptables: + # iptables -F + + For ip6tables: + # ip6tables -F + + + + + + + Ensure an nftables table exists + + Tables hold chains. Each table only has one address family and only applies to packets of this family. Tables can have one of five families. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + nftables doesn't have any default tables. Without a table being build, nftables will not filter network traffic. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Run the following command to create a table in nftables + # nft create table inet <table name> + + + Example: + + # nft create table inet filter + + Impact: + + Adding rules to a running nftables can cause loss of connectivity to the system + + + + + + + + + + + + + + + + + + Ensure nftables base chains exist + + Chains are containers for rules. They exist in two kinds, base chains and regular chains. A base chain is an entry point for packets from the networking stack, a regular chain may be used as jump target and is used for better rule organization. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If a base chain doesn't exist with a hook for input, forward, and delete, packets that would flow through those chains will not be touched by nftables. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Run the following command to create the base chains: + # nft create chain inet <table name> <base chain name> { type filter hook <(input|forward|output)> priority 0 \; } + + + Example: + + +# nft create chain inet filter input { type filter hook input priority 0 \; }
+# nft create chain inet filter forward { type filter hook forward priority 0 \; }
+# nft create chain inet filter output { type filter hook output priority 0 \; } +
+ Impact: + + +If configuring nftables over ssh, creating + a base chain + with a policy of drop + will cause loss of connectivity. + Ensure that a rule allowing ssh has been added to the base chain prior to setting the base chain's policy to drop + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure nftables loopback traffic is configured + + Configure the loopback interface to accept traffic. Configure all other interfaces to deny traffic to the loopback network + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Loopback traffic is generated between processes on machine and is typically critical to operation of the system. The loopback interface is the only place that loopback network traffic should be seen, all other interfaces should ignore traffic on this network as an anti-spoofing measure. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Run the following commands to implement the loopback rules: + +# nft add rule inet filter input iif lo accept
+# nft create rule inet filter input ip saddr 127.0.0.0/8 counter drop +
+ + - IF - + IPv6 is enabled: + Run the following command to implement the IPv6 loopback rules: + # nft add rule inet filter input ip6 saddr ::1 counter drop + +
+
+
+ + + + + + + + + + +
+ + Ensure nftables outbound and established connections are configured + + Configure the firewall rules for new outbound and established connections + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If rules are not in place for new outbound and established connections, all packets will be dropped by the default policy preventing network usage. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Configure nftables in accordance with site policy. The following commands will implement a policy to allow all outbound connections and all established connections: + +# nft add rule inet filter input ip protocol tcp ct state established accept
+# nft add rule inet filter input ip protocol udp ct state established accept
+# nft add rule inet filter input ip protocol icmp ct state established accept
+# nft add rule inet filter output ip protocol tcp ct state new,related,established accept
+# nft add rule inet filter output ip protocol udp ct state new,related,established accept
+# nft add rule inet filter output ip protocol icmp ct state new,related,established accept +
+
+
+
+
+ + Ensure nftables default deny firewall policy + + Base chain policy is the default verdict that will be applied to packets reaching the end of the chain. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +There are two policies: accept (Default) and drop. If the policy is set to accept +, the firewall will accept any packet that is not configured to be denied and the packet will continue traversing the network stack. + It is easier to white list acceptable usage than to black list unacceptable usage. + + Note: + Changing firewall settings while connected over the network can result in being locked out of the system. + + + + Manual Page nft + NIST SP 800-53 Rev. 5: CA-9 + + + + Run the following command for the base chains with the input, forward, and output hooks to implement a default DROP policy: + # nft chain <table family> <table name> <chain name> { policy drop \; } + + + Example: + + +# nft chain inet filter input { policy drop \; }
+# nft chain inet filter forward { policy drop \; }
+# nft chain inet filter output { policy drop \; } +
+ Impact: + + If configuring nftables over ssh, creating a base chain with a policy of drop will cause loss of connectivity. + +Ensure that a rule allowing ssh + has been added to the base chain prior to setting the base chain's policy to drop + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure nftables service is enabled and active + + The nftables service allows for the loading of nftables rulesets during boot, or starting on the nftables service + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +The nftables service restores the nftables rules from the rules files referenced in the /etc/sysconfig/nftables.conf + file during boot or the starting of the nftables service + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + +Run the following commands to unmask, enable and start nftables.service +: + +# systemctl unmask nftables.service
+# systemctl --now enable nftables.service +
+
+
+
+ + + + + + + + + + + + + + + + +
+ + Ensure nftables rules are permanent + + nftables is a subsystem of the Linux kernel providing filtering and classification of network packets/datagrams/frames. + +The nftables service reads the /etc/sysconfig/nftables.conf + file for a nftables file or files to include in the nftables ruleset. + A nftables ruleset containing the input, forward, and output base chains allow network traffic to be filtered. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Changes made to nftables ruleset only affect the live system, you will also need to configure the nftables ruleset to apply on boot + + + + + + + +Edit the /etc/sysconfig/nftables.conf + file and un-comment or add a line with include <Absolute path to nftables rules file> + for each nftables file you want included in the nftables ruleset on boot: + + Example: + + include "/etc/nftables/nftables.rules" + + + + + + + + + + + + + + +
+ + Configure iptables + + + + If firewalld or nftables are being used in your environment, please follow the guidance in their respective section and pass-over the guidance in this section. + + + IPtables is an application that allows a system administrator to configure the IPv4 and IPv6 tables, chains and rules provided by the Linux kernel firewall. While several methods of configuration exist this section is intended only to ensure the resulting IPtables rules are in place, not how they are configured. If IPv6 is in use in your environment, similar settings should be applied to the IP6tables as well. + + + Note: + Configuration of a live systems firewall directly over a remote connection will often result in being locked out. It is advised to have a known good firewall configuration set to run on boot and to configure an entire firewall structure in a script that is then run and tested before saving to boot. + + + + Configure iptables software + + This section provides guidance for installing, enabling, removing, and disabling software packages necessary for using IPTables as the method for configuring and maintaining a Host Based Firewall on the system. + + Note: + Using more than one method to configure and maintain a Host Based Firewall can cause unexpected results. If FirewallD or NFTables are being used for configuration and maintenance, this section should be skipped and the guidance in their respective section followed. + + + Ensure iptables packages are installed + + iptables is a utility program that allows a system administrator to configure the tables provided by the Linux kernel firewall, implemented as different Netfilter modules, and the chains and rules it stores. Different kernel modules and programs are used for different protocols; iptables applies to IPv4, ip6tables to IPv6, arptables to ARP, and ebtables to Ethernet frames. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + A method of configuring and maintaining firewall rules is necessary to configure a Host Based Firewall. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + +Run the following command to install iptables + and iptables-services + + # yum install iptables iptables-services + + + + + + + + + + + + + + + + + + + + + + Configure iptables + + IPTables is used to set up, maintain, and inspect the tables of IP packet filter rules in the Linux kernel. Several different tables may be defined. Each table contains a number of built-in chains and may also contain user-defined chains. + Each chain is a list of rules which can match a set of packets. Each rule specifies what to do with a packet that matches. This is called a 'target', which may be a jump to a user-defined chain in the same table. + + Note: + + + This section broadly assumes starting with an empty IPTables firewall ruleset (established by flushing the rules with iptables -F). + Configuration of a live systems firewall directly over a remote connection will often result in being locked out. + It is advised to have a known good firewall configuration set to run on boot and to configure an entire firewall structure in a script that is then run and tested before saving to boot. + + +The following script will implement the firewall rules of this section and open port 22(ssh) from anywhere. This needs to be updated to only allow systems requiring ssh connectivity to connect as per site policy. + + +#!/bin/bash
+
+# Flush IPtables rules
+iptables -F
+
+# Ensure default deny firewall policy
+iptables -P INPUT DROP
+iptables -P OUTPUT DROP
+iptables -P FORWARD DROP
+
+# Ensure loopback traffic is configured
+iptables -A INPUT -i lo -j ACCEPT
+iptables -A OUTPUT -o lo -j ACCEPT
+iptables -A INPUT -s 127.0.0.0/8 -j DROP
+
+# Ensure outbound and established connections are configured
+iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
+iptables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT
+iptables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT
+iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
+iptables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
+iptables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT
+
+# Open inbound ssh(tcp port 22) connections
+iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT +
+
+ + Ensure iptables loopback traffic is configured + + Configure the loopback interface to accept traffic. Configure all other interfaces to deny traffic to the loopback network (127.0.0.0/8). + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Loopback traffic is generated between processes on machine and is typically critical to operation of the system. The loopback interface is the only place that loopback network (127.0.0.0/8) traffic should be seen, all other interfaces should ignore traffic on this network as an anti-spoofing measure. + + Note: Changing firewall settings while connected over network can result in being locked out of the system. + + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Run the following commands to implement the loopback rules: + +# iptables -A INPUT -i lo -j ACCEPT
+# iptables -A OUTPUT -o lo -j ACCEPT
+# iptables -A INPUT -s 127.0.0.0/8 -j DROP +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure iptables outbound and established connections are configured + + Configure the firewall rules for new outbound, and established connections. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If rules are not in place for new outbound, and established connections all packets will be dropped by the default policy preventing network usage. + + Note: Changing firewall settings while connected over network can result in being locked out of the system. + + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Configure iptables in accordance with site policy. The following commands will implement a policy to allow all outbound connections and all established connections: + +# iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
+# iptables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT
+# iptables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT
+# iptables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
+# iptables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
+# iptables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT +
+
+
+
+
+ + Ensure iptables rules exist for all open ports + + Any ports that have been opened on non-loopback addresses need firewall rules to govern traffic. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + Devices + Protect + + + + + + Without a firewall rule configured for open ports default firewall policy will drop all packets to these ports. + + Note: + + + Changing firewall settings while connected over network can result in being locked out of the system. + The remediation command opens up the port to traffic from all sources. Consult iptables documentation and set any restrictions in compliance with site policy. + + + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + For each port identified in the audit which does not have a firewall rule establish a proper rule for accepting inbound connections: + # iptables -A INPUT -p <protocol> --dport <port> -m state --state NEW -j ACCEPT + + + + + + + + + + + + + + + + + Ensure iptables default deny firewall policy + + A default deny all policy on connections ensures that any unconfigured network usage will be rejected. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + With a default accept policy the firewall will accept any packet that is not configured to be denied. It is easier to white list acceptable usage than to black list unacceptable usage. + + Note: + Changing firewall settings while connected over network can result in being locked out of the system. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Run the following commands to implement a default DROP policy: + +# iptables -P INPUT DROP
+# iptables -P OUTPUT DROP
+# iptables -P FORWARD DROP +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure iptables rules are saved + + +The iptables-services + package includes the /etc/sysconfig/iptables + file. The iptables + rules in this file will be loaded by the iptables.service + during boot, or when it is started or re-loaded. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +If the iptables + rules are not saved and a system re-boot occurs, the iptables + rules will be lost. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + +Run the following commands to create or update the /etc/sysconfig/iptables + file: + +Run the following command to review the current running iptables + configuration: + # iptables -L + + Output should include: + +Chain INPUT (policy DROP)
+target prot opt source destination
+ACCEPT all -- anywhere anywhere
+DROP all -- loopback/8 anywhere
+ACCEPT tcp -- anywhere anywhere state ESTABLISHED
+ACCEPT udp -- anywhere anywhere state ESTABLISHED
+ACCEPT icmp -- anywhere anywhere state ESTABLISHED
+ACCEPT tcp -- anywhere anywhere tcp dpt:ssh state NEW
+
+Chain FORWARD (policy DROP)
+target prot opt source destination
+
+Chain OUTPUT (policy DROP)
+target prot opt source destination
+ACCEPT all -- anywhere anywhere
+ACCEPT tcp -- anywhere anywhere state NEW,ESTABLISHED
+ACCEPT udp -- anywhere anywhere state NEW,ESTABLISHED
+ACCEPT icmp -- anywhere anywhere state NEW,ESTABLISHED +
+ +Run the following command to save the verified running configuration to the file /etc/sysconfig/iptables +: + +# service iptables save
+
+iptables: Saving firewall rules to /etc/sysconfig/iptables:[ OK ] +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure iptables service is enabled and active + + + iptables.service + is a utility for configuring and maintaining iptables +. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + + iptables.service + will load the iptables rules saved in the file /etc/sysconfig/iptables + at boot, otherwise the iptables rules will be cleared during a re-boot of the system. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Run the following command to enable and start iptables: + +# systemctl unmask iptables.service
+# systemctl --now enable iptables.service +
+
+
+
+ + + + + + + + + + + + + + + + +
+
+ + Configure ip6tables + + + If IPv6 is not enabled on the system, this section can be skipped. + + Ip6tables is used to set up, maintain, and inspect the tables of IPv6 packet filter rules in the Linux kernel. Several different tables may be defined. Each table contains a number of built-in chains and may also contain user-defined chains. +Each chain is a list of rules which can match a set of packets. Each rule specifies what to do with a packet that matches. This is called a `target', which may be a jump to a user-defined chain in the same table. + + Note: + + + This section broadly assumes starting with an empty ip6tables firewall ruleset (established by flushing the rules with ip6tables -F). + Configuration of a live systems firewall directly over a remote connection will often result in being locked out. It is advised to have a known good firewall configuration set to run on boot and to configure an entire firewall structure in a script that is then run and tested before saving to boot. + + +The following script will implement the firewall rules of this section and open port 22(ssh) from anywhere. This needs to be updated to only allow systems requiring ssh connectivity to connect as per site policy. + + +#!/bin/bash
+
+# Flush ip6tables rules
+ip6tables -F
+
+# Ensure default deny firewall policy
+ip6tables -P INPUT DROP
+ip6tables -P OUTPUT DROP
+ip6tables -P FORWARD DROP
+
+# Ensure loopback traffic is configured
+ip6tables -A INPUT -i lo -j ACCEPT
+ip6tables -A OUTPUT -o lo -j ACCEPT
+ip6tables -A INPUT -s ::1 -j DROP
+
+# Ensure outbound and established connections are configured
+ip6tables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
+ip6tables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT
+ip6tables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT
+ip6tables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
+ip6tables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
+ip6tables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT
+
+# Open inbound ssh(tcp port 22) connections
+ip6tables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT +
+
+ + Ensure ip6tables loopback traffic is configured + + Configure the loopback interface to accept traffic. Configure all other interfaces to deny traffic to the loopback network (::1). + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Loopback traffic is generated between processes on machine and is typically critical to operation of the system. The loopback interface is the only place that loopback network (::1) traffic should be seen, all other interfaces should ignore traffic on this network as an anti-spoofing measure. + + Note: + Changing firewall settings while connected over network can result in being locked out of the system. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Run the following commands to implement the loopback rules: + +# ip6tables -A INPUT -i lo -j ACCEPT
+# ip6tables -A OUTPUT -o lo -j ACCEPT
+# ip6tables -A INPUT -s ::1 -j DROP +
+
+
+
+ + + + + + + + + + + + + + + + +
+ + Ensure ip6tables outbound and established connections are configured + + Configure the firewall rules for new outbound, and established IPv6 connections. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + If rules are not in place for new outbound, and established connections all packets will be dropped by the default policy preventing network usage. + + Note: + Changing firewall settings while connected over network can result in being locked out of the system. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Configure iptables in accordance with site policy. The following commands will implement a policy to allow all outbound connections and all established connections: + +# ip6tables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
+# ip6tables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT
+# ip6tables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED -j ACCEPT
+# ip6tables -A INPUT -p tcp -m state --state ESTABLISHED -j ACCEPT
+# ip6tables -A INPUT -p udp -m state --state ESTABLISHED -j ACCEPT
+# ip6tables -A INPUT -p icmp -m state --state ESTABLISHED -j ACCEPT +
+
+
+
+
+ + Ensure ip6tables firewall rules exist for all open ports + + Any ports that have been opened on non-loopback addresses need firewall rules to govern traffic. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Without a firewall rule configured for open ports default firewall policy will drop all packets to these ports. + + Note: + + + Changing firewall settings while connected over network can result in being locked out of the system. + The remediation command opens up the port to traffic from all sources. Consult iptables documentation and set any restrictions in compliance with site policy. + + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + For each port identified in the audit which does not have a firewall rule establish a proper rule for accepting inbound connections: + # ip6tables -A INPUT -p <protocol> --dport <port> -m state --state NEW -j ACCEPT + + + + + + + + + + + + + + + + + + + + + + + Ensure ip6tables default deny firewall policy + + A default deny all policy on connections ensures that any unconfigured network usage will be rejected. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + With a default accept policy the firewall will accept any packet that is not configured to be denied. It is easier to white list acceptable usage than to black list unacceptable usage. + + Note: Changing firewall settings while connected over network can result in being locked out of the system. + + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + Run the following commands to implement a default DROP policy: + +# ip6tables -P INPUT DROP
+# ip6tables -P OUTPUT DROP
+# ip6tables -P FORWARD DROP +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure ip6tables rules are saved + + +The iptables-services + package includes the /etc/sysconfig/ip6tables + file. The ip6tables + rules in this file will be loaded by the ip6tables.service + during boot, or when it is started or re-loaded. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + +If the ip6tables + rules are not saved and a system re-boot occurs, the ip6tables + rules will be lost. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + +Run the following commands to create or update the /etc/sysconfig/ip6tables + file: + +Run the following command to review the current running iptables + configuration: + # ip6tables -L + + Output should include: + +Chain INPUT (policy DROP)
+target prot opt source destination
+ACCEPT all anywhere anywhere
+DROP all localhost anywhere
+ACCEPT tcp anywhere anywhere state ESTABLISHED
+ACCEPT udp anywhere anywhere state ESTABLISHED
+ACCEPT icmp anywhere anywhere state ESTABLISHED
+ACCEPT tcp anywhere anywhere tcp dpt:ssh state NEW
+
+Chain FORWARD (policy DROP)
+target prot opt source destination
+
+Chain OUTPUT (policy DROP)
+target prot opt source destination
+ACCEPT all anywhere anywhere
+ACCEPT tcp anywhere anywhere state NEW,ESTABLISHED
+ACCEPT udp anywhere anywhere state NEW,ESTABLISHED
+ACCEPT icmp anywhere anywhere state NEW,ESTABLISHED +
+ +Run the following command to save the verified running configuration to the file /etc/sysconfig/ip6tables +: + +# service ip6tables save
+
+ip6tables: Saving firewall rules to /etc/sysconfig/ip6table[ OK ] +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure ip6tables is enabled and active + + + ip6tables.service + is a utility for configuring and maintaining ip6tables +. + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + + ip6tables.service + will load the iptables rules saved in the file /etc/sysconfig/ip6tables + at boot, otherwise the ip6tables rules will be cleared during a re-boot of the system. + + + + NIST SP 800-53 Rev. 5: CA-9 + + + + +Run the following commands to unmask, enable and start ip6tables.service +: + +# systemctl unmask ip6tables.service
+# systemctl --now start ip6tables.service +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + Access, Authentication and Authorization + + + Configure job schedulers + + A job scheduler is used to execute jobs, commands, or shell scripts, at fixed times, dates, or intervals + + + Configure cron + + + cron + is a time based job scheduler + + Note: + + + +Other methods, such as systemd timers +, exist for scheduling jobs. If another method is used, cron + should be removed, and the alternate method should be secured in accordance with local site policy + + -IF- + cron is not installed on the system, this sub section can be skipped + + + + Ensure cron daemon is enabled and active + + +The cron + daemon is used to execute batch jobs on the system. + + + +While there may not be user jobs that need to be run on the system, the system does have maintenance jobs that may include security monitoring that have to run, and cron + is used to execute them. + + Note: + IF systemd timers are configured and used for scheduled tasks, this recommendation may be skipped + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Run the following commands to unmask, enable, and start crond +: + +# systemctl unmask crond
+# systemctl --now enable crond +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/crontab are configured + + +The /etc/crontab + file is used by cron + to control its own jobs. The commands in this item make sure that root is the user and group owner of the file and that only the owner can access the file. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + This file contains information on what system jobs are run by cron. Write access to these files could provide unprivileged users with the ability to elevate their privileges. Read access to these files could provide users with the ability to gain insight on system jobs that run on the system and could provide them a way to gain unauthorized privileged access. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set ownership and permissions on /etc/crontab +: + +# chown root:root /etc/crontab
+# chmod og-rwx /etc/crontab +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure permissions on /etc/cron.hourly are configured + + +This directory contains system cron + jobs that need to run on an hourly basis. The files in this directory cannot be manipulated by the crontab + command, but are instead edited by system administrators using a text editor. The commands below restrict read/write and search access to user and group root, preventing regular users from accessing this directory. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Granting write access to this directory for non-privileged users could provide them the means for gaining unauthorized elevated privileges. Granting read access to this directory could give an unprivileged user insight in how to gain elevated privileges or circumvent auditing controls. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + +Run the following commands to set ownership and permissions on the /etc/cron.hourly + directory: + +# chown root:root /etc/cron.hourly/
+# chmod og-rwx /etc/cron.hourly/ +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure permissions on /etc/cron.daily are configured + + +The /etc/cron.daily + directory contains system cron jobs that need to run on a daily basis. The files in this directory cannot be manipulated by the crontab + command, but are instead edited by system administrators using a text editor. The commands below restrict read/write and search access to user and group root, preventing regular users from accessing this directory. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Granting write access to this directory for non-privileged users could provide them the means for gaining unauthorized elevated privileges. Granting read access to this directory could give an unprivileged user insight in how to gain elevated privileges or circumvent auditing controls. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + +Run the following commands to set ownership and permissions on the /etc/cron.daily + directory: + +# chown root:root /etc/cron.daily/
+# chmod og-rwx /etc/cron.daily/ +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure permissions on /etc/cron.weekly are configured + + +The /etc/cron.weekly + directory contains system cron jobs that need to run on a weekly basis. The files in this directory cannot be manipulated by the crontab + command, but are instead edited by system administrators using a text editor. The commands below restrict read/write and search access to user and group root, preventing regular users from accessing this directory. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Granting write access to this directory for non-privileged users could provide them the means for gaining unauthorized elevated privileges. Granting read access to this directory could give an unprivileged user insight in how to gain elevated privileges or circumvent auditing controls. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + +Run the following commands to set ownership and permissions on the /etc/cron.weekly + directory: + +# chown root:root /etc/cron.weekly/
+# chmod og-rwx /etc/cron.weekly/ +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure permissions on /etc/cron.monthly are configured + + +The /etc/cron.monthly + directory contains system cron jobs that need to run on a monthly basis. The files in this directory cannot be manipulated by the crontab + command, but are instead edited by system administrators using a text editor. The commands below restrict read/write and search access to user and group root, preventing regular users from accessing this directory. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Granting write access to this directory for non-privileged users could provide them the means for gaining unauthorized elevated privileges. Granting read access to this directory could give an unprivileged user insight in how to gain elevated privileges or circumvent auditing controls. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + +Run the following commands to set ownership and permissions on the /etc/cron.monthly + directory: + +# chown root:root /etc/cron.monthly/
+# chmod og-rwx /etc/cron.monthly/ +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure permissions on /etc/cron.d are configured + + +The /etc/cron.d + directory contains system cron + jobs that need to run in a similar manner to the hourly, daily weekly and monthly jobs from /etc/crontab +, but require more granular control as to when they run. The files in this directory cannot be manipulated by the crontab + command, but are instead edited by system administrators using a text editor. The commands below restrict read/write and search access to user and group root, preventing regular users from accessing this directory. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Granting write access to this directory for non-privileged users could provide them the means for gaining unauthorized elevated privileges. Granting read access to this directory could give an unprivileged user insight in how to gain elevated privileges or circumvent auditing controls. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + +Run the following commands to set ownership and permissions on the /etc/cron.d + directory: + +# chown root:root /etc/cron.d/
+# chmod og-rwx /etc/cron.d/ +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure crontab is restricted to authorized users + + + crontab + is the program used to install, deinstall, or list the tables used to drive the cron daemon. Each user can have their own crontab, and though these are files in /var/spool/cron/crontabs +, they are not intended to be edited directly. + +If the /etc/cron.allow + file exists, then you must be listed (one user per line) therein in order to be allowed to use this command. If the /etc/cron.allow + file does not exist but the /etc/cron.deny + file does exist, then you must not be listed in the /etc/cron.deny + file in order to use this command. + If neither of these files exists, then depending on site-dependent configuration parameters, only the super user will be allowed to use this command, or all users will be able to use this command. + +If both files exist then /etc/cron.allow + takes precedence. Which means that /etc/cron.deny + is not considered and your user must be listed in /etc/cron.allow + in order to be able to use the crontab. + Regardless of the existence of any of these files, the root administrative user is always allowed to setup a crontab. + +The files /etc/cron.allow + and /etc/cron.deny +, if they exist, must be either world-readable, or readable by group crontab +. If they are not, then cron will deny access to all users until the permissions are fixed. + +There is one file for each user's crontab under the /var/spool/cron/crontabs + directory. Users are not allowed to edit the files under that directory directly to ensure that only users allowed by the system to run periodic tasks can add them, and only syntactically correct crontabs will be written there. This is enforced by having the directory writable only by the crontab + group and configuring crontab command with the setgid bid set for that specific group. + + Note: + + + +Even though a given user is not listed in cron.allow +, cron jobs can still be run as that user + +The files /etc/cron.allow + and /etc/cron.deny +, if they exist, only controls administrative access to the crontab command for scheduling and modifying cron jobs + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +On many systems, only the system administrator is authorized to schedule cron + jobs. Using the cron.allow + file to control who can run cron + jobs enforces this policy. It is easier to manage an allow list than a deny list. In a deny list, you could potentially add a user ID to the system and forget to add it to the deny files. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + -IF- + cron is installed on the system: + Run the following commands to: + + +Create /etc/cron.allow + if it doesn't exist + +Change owner or user root + + +Change group owner to group root + + +Change mode to 640 + or more restrictive + + +# [ ! -e "/etc/cron.allow" ] && touch /etc/cron.allow
+# chown root:root /etc/cron.allow
+# chmod u-x,g-wx,o-rwx /etc/cron.allow +
+ Run the following commands to: + + -IF- + /etc/cron.deny + exists: + + +Change owner or user root + + +Change group owner to group root + + +Change mode to 640 + or more restrictive + + +# [ -e "/etc/cron.deny" ] && chown root:root /etc/cron.deny
+# [ -e "/etc/cron.deny" ] && chmod u-x,g-wx,o-rwx /etc/cron.deny +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ + Configure at + + + at + is a command-line utility used to schedule a job for later execution + + Note: + if at + is not installed on the system, this section can be skipped + + + Ensure at is restricted to authorized users + + + at + allows fairly complex time specifications, extending the POSIX.2 standard. It accepts times of the form HH:MM to run a job at a specific time of day. (If that time is already past, the next day is assumed.) You may also specify midnight, noon, or teatime (4pm) and you can have a time-of-day suffixed with AM or PM for running in the morning or the evening. You can also say what day the job will be run, by giving a date in the form month-name day with an optional year, or giving a date of the form MMDD[CC]YY, MM/DD/[CC]YY, DD.MM.[CC]YY or [CC]YY-MM-DD. The specification of a date must follow the specification of the time of day. You can also give times like now + count time-units, where the time-units can be minutes, hours, days, or weeks and you can tell at to run the job today by suffixing the time with today and to run the job tomorrow by suffixing the time with tomorrow. + +The /etc/at.allow + and /etc/at.deny + files determine which user can submit commands for later execution via at or batch. The format of the files is a list of usernames, one on each line. Whitespace is not permitted. If the file /etc/at.allow + exists, only usernames mentioned in it are allowed to use at. If /etc/at.allow + does not exist, /etc/at.deny + is checked, every username not mentioned in it is then allowed to use at. An empty /etc/at.deny + means that every user may use at. If neither file exists, only the superuser is allowed to use at. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +On many systems, only the system administrator is authorized to schedule at + jobs. Using the at.allow + file to control who can run at + jobs enforces this policy. It is easier to manage an allow list than a deny list. In a deny list, you could potentially add a user ID to the system and forget to add it to the deny files. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + -IF- + at is installed on the system: + Run the following script to: + + + /etc/at.allow +: + + Create the file if it doesn't exist + +Change owner or user root + + +If group daemon + exists, change to group daemon +, else change group to root + + +Change mode to 640 + or more restrictive + + + + -IF- + /etc/at.deny + exists: + + +Change owner or user root + + +If group daemon + exists, change to group daemon +, else change group to root + + +Change mode to 640 + or more restrictive + + + + +#!/usr/bin/env bash
+
+{
+ grep -Pq -- '^daemon\b' /etc/group && l_group="daemon" || l_group="root"
+ [ ! -e "/etc/at.allow" ] && touch /etc/at.allow
+ chown root:"$l_group" /etc/at.allow
+ chmod u-x,g-wx,o-rwx /etc/at.allow
+ [ -e "/etc/at.deny" ] && chown root:"$l_group" /etc/at.deny
+ [ -e "/etc/at.deny" ] && chmod u-x,g-wx,o-rwx /etc/at.deny
+} +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + Configure SSH Server + + +SSH is a secure, encrypted replacement for common login services such as telnet +, ftp +, rlogin +, rsh +, and rcp +. It is strongly recommended that sites abandon older clear-text login protocols and use SSH to prevent session hijacking and sniffing of sensitive data off the network. + + Note: + + + +The recommendations in this section only apply if the SSH daemon is installed on the system, if remote access is not + required the SSH daemon can be removed and this section skipped. + +The openSSH daemon configuration option Match +, may cause the audits in this section's recommendations to report incorrectly. It is recommended that this option only be used if it's needed and fully understood. If this option is configured in accordance with local site policy, it should be accounted for when following the recommendations in this section. + +The audits of the configuration in this section are run in the context of the root user, the local host name, and the local host's IP address. If a Match + block exists that matches one of these criteria, the output of the audit will be from the match block. The respective matched criteria should be replaced with a non-matching substitution. + +Once all configuration changes have been made to /etc/ssh/sshd_config + the sshd + configuration must be reloaded + + Match +: + + Introduces a conditional block. + If all of the criteria on the Match line are satisfied, the keywords on the following lines override those set in the global section of the config file, until either another Match line or the end of the file. + If a keyword appears in multiple Match blocks that are satisfied, only the first instance of the keyword is applied. + The arguments to Match are one or more criteria-pattern pairs or the single token All which matches all criteria. The available criteria are User, Group, Host, LocalAddress, LocalPort, RDomain, and Address (with RDomain representing the rdomain(4) on which the connection was received). + The match patterns may consist of single entries or comma-separated lists and may use the wildcard and negation operators described in the PATTERNS section of ssh_config(5). + The patterns in an Address criteria may additionally contain addresses to match in CIDR address/masklen format, such as 192.0.2.0/24 or 2001:db8::/32. Note that the mask length provided must be consistent with the address - it is an error to specify a mask length that is too long for the address or one with bits set in this host portion of the address. For example, 192.0.2.0/33 and 192.0.2.0/8, respectively. + Only a subset of keywords may be used on the lines following a Match keyword. + +Available keywords are: AcceptEnv +, AllowAgentForwarding +, AllowGroups +, AllowStreamLocalForwarding +, AllowTcpForwarding +, AllowUsers +, AuthenticationMethods +, AuthorizedKeysCommand +, AuthorizedKeysCommandUser +, AuthorizedKeysFile +, AuthorizedPrincipalsCommand +, AuthorizedPrincipalsCommandUser +, AuthorizedPrincipalsFile +, Banner +, ChrootDirectory +, ClientAliveCountMax +, ClientAliveInterval +, DenyGroups +, DenyUsers +, ForceCommand +, GatewayPorts +, GSSAPIAuthentication +, HostbasedAcceptedKeyTypes +, HostbasedAuthentication +, HostbasedUsesNameFromPacketOnly +, IPQoS +, KbdInteractiveAuthentication +, KerberosAuthentication +, KerberosUseKuserok +, MaxAuthTries +, MaxSessions +, PasswordAuthentication +, PermitEmptyPasswords +, PermitOpen +, PermitRootLogin +, PermitTTY +, PermitTunnel +, PermitUserRC +, PubkeyAcceptedKeyTypes +, PubkeyAuthentication +, RekeyLimit +, RevokedKeys +, StreamLocalBindMask +, StreamLocalBindUnlink +, TrustedUserCAKeys +, X11DisplayOffset +, X11MaxDisplays +, X11Forwarding + and X11UseLocalhost +. + + + + Command to re-load the SSH daemon configuration: + # systemctl reload sshd + + Command to remove the SSH daemon: + # yum remove openssh-server + + + + Ensure permissions on /etc/ssh/sshd_config are configured + + +The file /etc/ssh/sshd_config +, and files ending in .conf + in the /etc/ssh/sshd_config.d + directory, contain configuration specifications for sshd +. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +configuration specifications for sshd + need to be protected from unauthorized changes by non-privileged users. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + +Run the following script to set ownership and permissions on /etc/ssh/sshd_config + and files ending in .conf + in the /etc/ssh/sshd_config.d + directory: + +#!/usr/bin/env bash
+
+{
+ chmod u-x,og-rwx /etc/ssh/sshd_config
+ chown root:root /etc/ssh/sshd_config
+ while IFS= read -r -d $'\0' l_file; do
+ if [ -e "$l_file" ]; then
+ chmod u-x,og-rwx "$l_file"
+ chown root:root "$l_file"
+ fi
+ done < <(find /etc/ssh/sshd_config.d -type f -print0)
+} +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure permissions on SSH private host key files are configured + + An SSH private key is one of two files used in SSH public key authentication. In this authentication method, the possession of the private key is proof of identity. Only a private key that corresponds to a public key will be able to authenticate successfully. The private keys need to be stored and handled carefully, and no copies of the private key should be distributed. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + If an unauthorized user obtains the private SSH host key file, the host could be impersonated + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + Run the following script to set mode, ownership, and group on the private SSH host key files: + +#!/usr/bin/env bash
+
+{
+ l_output="" l_output2=""
+ l_skgn="$(grep -Po -- '^(ssh_keys|_?ssh)\b' /etc/group)" # Group designated to own openSSH keys
+ l_skgid="$(awk -F: '($1 == "'"$l_skgn"'"){print $3}' /etc/group)" # Get gid of group
+ if [ -n "$l_skgid" ]; then
+ l_agroup="(root|$l_skgn)" && l_sgroup="$l_skgn" && l_mfix="u-x,g-wx,o-rwx"
+ else
+ l_agroup="root" && l_sgroup="root" && l_mfix="u-x,go-rwx"
+ fi
+ if command -v ssh-keygen &>/dev/null; then
+ unset a_skarr && a_skarr=() # Clear and initialize array
+ if [ -d /etc/ssh ]; then
+ while IFS= read -r -d $'\0' l_file; do # Loop to populate array
+ l_var="$(ssh-keygen -l -f 2>/dev/null "$l_file")"
+ if [ -n "$l_var" ] && ! grep -Pq -- '\h+no\h+comment\b' <<< "$l_var"; then
+ a_skarr+=("$(stat -Lc '%n^%#a^%U^%G^%g' "$l_file")")
+ fi
+ done < <(find -L /etc/ssh -xdev -type f -print0)
+ if (( ${#a_skarr[@]} > 0 )); then
+ while IFS="^" read -r l_file l_mode l_owner l_group l_gid; do
+ l_out2=""
+ [ "$l_gid" = "$l_skgid" ] && l_pmask="0137" || l_pmask="0177"
+ l_maxperm="$( printf '%o' $(( 0777 & ~$l_pmask )) )"
+ if [ $(( $l_mode & $l_pmask )) -gt 0 ]; then
+ l_out2="$l_out2\n - Mode: \"$l_mode\" should be mode: \"$l_maxperm\" or more restrictive\n - Revoking excess permissions"
+ chmod "$l_mfix" "$l_file"
+ fi
+ if [ "$l_owner" != "root" ]; then
+ l_out2="$l_out2\n - Owned by: \"$l_owner\" should be owned by \"root\"\n - Changing ownership to \"root\""
+ chown root "$l_file"
+ fi
+ if [[ ! "$l_group" =~ $l_agroup ]]; then
+ l_out2="$l_out2\n - Owned by group \"$l_group\" should be group owned by: \"${l_agroup//|/ or }\"\n - Changing group ownership to \"$l_sgroup\""
+ chgrp "$l_sgroup" "$l_file"
+ fi
+ [ -n "$l_out2" ] && l_output2="$l_output2\n - File: \"$l_file\"$l_out2"
+ done <<< "$(printf '%s\n' "${a_skarr[@]}")"
+ else
+ l_output=" - No private keys found in \"/etc/ssh\""
+ fi
+ else
+ l_output="- ssh directory not found on the system"
+ fi
+ unset a_skarr
+ else
+ l_output2=" - ssh-keygen command not found\n - manual remediation may be required"
+ fi
+ if [ -z "$l_output2" ]; then
+ echo -e "\n- No access changes required\n"
+ else
+ echo -e "\n- Remediation results:\n$l_output2\n"
+ fi
+} +
+
+
+
+ + + + + + + + + +
+ + Ensure permissions on SSH public host key files are configured + + An SSH public key is one of two files used in SSH public key authentication. In this authentication method, a public key is a key that can be used for verifying digital signatures generated using a corresponding private key. Only a public key that corresponds to a private key will be able to authenticate successfully. + + + + + + + Data + Protect + + + + + + Applications + Protect + + + + + + If a public host key file is modified by an unauthorized user, the SSH service may be compromised. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + Run the following script to set mode, ownership, and group on the public SSH host key files: + +#!/usr/bin/env bash
+
+{
+ l_output="" l_output2=""
+ l_skgn="$(grep -Po -- '^(ssh_keys|_?ssh)\b' /etc/group)" # Group designated to own openSSH keys
+ l_skgid="$(awk -F: '($1 == "'"$l_skgn"'"){print $3}' /etc/group)" # Get gid of group
+ l_mfix="u-x,go-wx"
+ if command -v ssh-keygen &>/dev/null; then
+ unset a_skarr && a_skarr=() # Clear and initialize array
+ if [ -d /etc/ssh ]; then
+ while IFS= read -r -d $'\0' l_file; do # Loop to populate array
+ if grep -Pq -- '\h+no\h+comment\b' <<< "$(ssh-keygen -l -f 2>/dev/null "$l_file")"; then
+ a_skarr+=("$(stat -Lc '%n^%#a^%U^%G^%g' "$l_file")")
+ fi
+ done < <(find -L /etc/ssh -xdev -type f -print0)
+ if (( ${#a_skarr[@]} > 0 )); then
+ while IFS="^" read -r l_file l_mode l_owner l_group l_gid; do
+ l_out2=""
+ l_pmask="0133"
+ l_maxperm="$( printf '%o' $(( 0777 & ~$l_pmask )) )"
+ if [ $(( $l_mode & $l_pmask )) -gt 0 ]; then
+ l_out2="$l_out2\n - Mode: \"$l_mode\" should be mode: \"$l_maxperm\" or more restrictive\n - Revoking excess permissions"
+ chmod "$l_mfix" "$l_file"
+ fi
+ if [ "$l_owner" != "root" ]; then
+ l_out2="$l_out2\n - Owned by: \"$l_owner\" should be owned by \"root\"\n - Changing ownership to \"root\""
+ chown root "$l_file"
+ fi
+ if [[ ! "$l_group" =~ $l_agroup ]]; then
+ l_out2="$l_out2\n - Owned by group \"$l_group\" should be group owned by: \"${l_agroup//|/ or }\"\n - Changing group ownership to \"$l_sgroup\""
+ chgrp "$l_sgroup" "$l_file"
+ fi
+ [ -n "$l_out2" ] && l_output2="$l_output2\n - File: \"$l_file\"$l_out2"
+ done <<< "$(printf '%s\n' "${a_skarr[@]}")"
+ else
+ l_output=" - No public keys found in \"/etc/ssh\""
+ fi
+ else
+ l_output="- ssh directory not found on the system"
+ fi
+ unset a_skarr
+ else
+ l_output2=" - ssh-keygen command not found\n - manual remediation may be required"
+ fi
+ if [ -z "$l_output2" ]; then
+ echo -e "\n- No access changes required\n"
+ else
+ echo -e "\n- Remediation results:\n$l_output2\n"
+ fi
+} +
+
+
+
+ + + + + + + + + +
+ + Ensure sshd access is configured + + There are several options available to limit which users and group can access the system via SSH. It is recommended that at least one of the following options be leveraged: + + + AllowUsers +: + + +The AllowUsers + variable gives the system administrator the option of allowing specific users to ssh + into the system. The list consists of space separated user names. Numeric user IDs are not recognized with this variable. If a system administrator wants to restrict user access further by only allowing the allowed users to log in from a particular host, the entry can be specified in the form of user@host. + + + + AllowGroups +: + + +The AllowGroups + variable gives the system administrator the option of allowing specific groups of users to ssh + into the system. The list consists of space separated group names. Numeric group IDs are not recognized with this variable. + + + + DenyUsers +: + + +The DenyUsers + variable gives the system administrator the option of denying specific users to ssh + into the system. The list consists of space separated user names. Numeric user IDs are not recognized with this variable. If a system administrator wants to restrict user access further by specifically denying a user's access from a particular host, the entry can be specified in the form of user@host. + + + + DenyGroups +: + + +The DenyGroups + variable gives the system administrator the option of denying specific groups of users to ssh + into the system. The list consists of space separated group names. Numeric group IDs are not recognized with this variable. + + + + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + Restricting which users can remotely access the system via SSH will help ensure that only authorized users access the system. + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + +Edit /etc/ssh/sshd_config + and set one or more of the parameters above any Match + set statements as follows: + +AllowUsers <userlist>
+-OR-
+AllowGroups <grouplist>
+-OR-
+DenyUsers <userlist>
+-OR-
+DenyGroups <grouplist> +
+ + Note: + First occurrence of a option takes precedence, Match + set statements withstanding. + Run the following command to reload the openSSH server daemon configuration: + # systemctl reload-or-try-restart sshd.service + +
+
+
+ + + + + + + + + + +
+ + Ensure sshd Banner is configured + + +The Banner + parameter specifies a file whose contents must be sent to the remote user before authentication is permitted. By default, no banner is displayed. + + + Banners are used to warn connecting users of the particular site's policy regarding connection. Presenting a warning message prior to the normal user login may assist the prosecution of trespassers on the computer system. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the parameter above any Match + entries as follows: + Banner /etc/issue.net + + + Note: + First occurrence of a option takes precedence, Match set statements withstanding. + + + + + + + + + + + + + + + + + + + + + Ensure sshd Ciphers are configured + + This variable limits the ciphers that SSH can use during communication. + + Note: + + + Some organizations may have stricter requirements for approved ciphers. + Ensure that ciphers used are in compliance with site policy. + +The only "strong" ciphers currently FIPS 140-2 compliant are: + + aes256-ctr + aes192-ctr + aes128-ctr + + + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Weak ciphers that are used for authentication to the cryptographic module cannot be relied upon to provide confidentiality or integrity, and system data may be compromised. + + The Triple DES ciphers, as used in SSH, have a birthday bound of approximately four billion blocks, which makes it easier for remote attackers to obtain clear text data via a birthday attack against a long-duration encrypted session, aka a "Sweet32" attack. + Error handling in the SSH protocol; Client and Server, when using a block cipher algorithm in Cipher Block Chaining (CBC) mode, makes it easier for remote attackers to recover certain plain text data from an arbitrary block of cipher text in an SSH session via unknown vectors. + + + + + https://nvd.nist.gov/vuln/detail/CVE-2016-2183 + https://www.openssh.com/txt/cbc.adv + https://nvd.nist.gov/vuln/detail/CVE-2008-5161 + https://www.openssh.com/txt/cbc.adv + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: SC-8 + + + + +Edit the /etc/ssh/sshd_config file and add or modify the Ciphers + line to contain a comma separated list of the site approved (Strong) Ciphers: + + Example: + + Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr + + + Note: + First occurrence of an option takes precedence. + + + + + + + + + + + + + + + + Ensure sshd ClientAliveInterval and ClientAliveCountMax are configured + + + Note: + To clarify, the two settings described below are only meant for idle connections from a protocol perspective and are not meant to check if the user is active or not. An idle user does not mean an idle connection. SSH does not and never had, intentionally, the capability to drop idle users. In SSH versions before 8.2p1 + there was a bug that caused these values to behave in such a manner that they were abused to disconnect idle users. This bug has been resolved in 8.2p1 + and thus it can no longer be abused disconnect idle users. + +The two options ClientAliveInterval + and ClientAliveCountMax + control the timeout of SSH sessions. Taken directly from man 5 sshd_config +: + + + + ClientAliveInterval + Sets a timeout interval in seconds after which if no data has been received from the client, sshd(8) will send a message through the encrypted channel to request a response from the client. The default is 0, indicating that these messages will not be sent to the client. + + + + ClientAliveCountMax + Sets the number of client alive messages which may be sent without sshd(8) receiving any messages back from the client. If this threshold is reached while client alive messages are being sent, sshd will disconnect the client, terminating the session. It is important to note that the use of client alive messages is very different from TCPKeepAlive. The client alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option en‐abled by TCPKeepAlive is spoofable. The client alive mechanism is valuable when the client or server depend on knowing when a connection has become unresponsive. +The default value is 3. If ClientAliveInterval is set to 15, and ClientAliveCountMax is left at the default, unresponsive SSH clients will be disconnected after approximately 45 seconds. Setting a zero ClientAliveCountMax disables connection termination. + + + + + + + https://bugzilla.redhat.com/show_bug.cgi?id=1873547 + + + https://github.com/openssh/openssh-portable/blob/V_8_9/serverloop.c#L137 + + + + + +In order to prevent resource exhaustion, appropriate values should be set for both ClientAliveInterval + and ClientAliveCountMax +. Specifically, looking at the source code, ClientAliveCountMax + must be greater than zero in order to utilize the ability of SSH to drop idle connections. If connections are allowed to stay open indefinitely, this can potentially be used as a DDOS attack or simple resource exhaustion could occur over unreliable networks. + The example set here is a 45 second timeout. Consult your site policy for network timeouts and apply as appropriate. + + https://man.openbsd.org/sshd_config + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the parameters above any Match + entries according to site policy. + + Example: + + +ClientAliveInterval 15
+ClientAliveCountMax 3 +
+ + Note: + First occurrence of a option takes precedence, Match set statements withstanding. +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure sshd DisableForwarding is enabled + + +The DisableForwarding + parameter disables all forwarding features, including X11, ssh-agent(1), TCP and StreamLocal. This option overrides all other forwarding-related options and may simplify restricted configurations. + + X11Forwarding provides the ability to tunnel X11 traffic through the connection to enable remote graphic connections. + ssh-agent is a program to hold private keys used for public key authentication. Through use of environment variables the agent can be located and automatically used for authentication when logging in to other machines using ssh. + SSH port forwarding is a mechanism in SSH for tunneling application ports from the client to the server, or servers to clients. It can be used for adding encryption to legacy applications, going through firewalls, and some system administrators and IT professionals use it for opening backdoors into the internal network from their home machines. + + + + + + + + Devices + Protect + + + + + + Devices + Protect + + + + + + Disable X11 forwarding unless there is an operational requirement to use X11 applications directly. There is a small risk that the remote X11 servers of users who are logged in via SSH with X11 forwarding could be compromised by other users on the X11 server. Note that even if X11 forwarding is disabled, users can always install their own forwarders. + anyone with root privilege on the the intermediate server can make free use of ssh-agent to authenticate them to other servers + Leaving port forwarding enabled can expose the organization to security risks and backdoors. SSH connections are protected with strong encryption. This makes their contents invisible to most deployed network monitoring and traffic filtering solutions. This invisibility carries considerable risk potential if it is used for malicious purposes such as data exfiltration. Cybercriminals or malware could exploit SSH to hide their unauthorized communications, or to exfiltrate stolen data from the target network. + + + + sshd_config(5) + NIST SP 800-53 Rev. 5: CM-7 + + + + +Edit the /etc/ssh/sshd_config + file to set the DisableForwarding + parameter to yes +: + DisableForwarding yes + + + Note: + First occurrence of a option takes precedence. + Impact: + + SSH tunnels are widely used in many corporate environments. In some environments the applications themselves may have very limited native support for security. By utilizing tunneling, compliance with SOX, HIPAA, PCI-DSS, and other standards can be achieved without having to modify the applications. + + + + + + + + + + + + + + + + + Ensure sshd GSSAPIAuthentication is disabled + + +The GSSAPIAuthentication + parameter specifies whether user authentication based on GSSAPI is allowed + + + Allowing GSSAPI authentication through SSH exposes the system's GSSAPI to remote hosts, and should be disabled to reduce the attack surface of the system + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the GSSAPIAuthentication + parameter to no + above any Match + statement: + GSSAPIAuthentication no + + + Note: + First occurrence of a option takes precedence, Match + set statements withstanding. + + + + + + + + + + + + + + + + + + + + + Ensure sshd HostbasedAuthentication is disabled + + +The HostbasedAuthentication + parameter specifies if authentication is allowed through trusted hosts via the user of .rhosts +, or /etc/hosts.equiv +, along with successful public key client host authentication. + + + +Even though the .rhosts + files are ineffective if support is disabled in /etc/pam.conf +, disabling the ability to use .rhosts + files in SSH provides an additional layer of protection. + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the HostbasedAuthentication + parameter to no +: + HostbasedAuthentication no + + + Note: + First occurrence of a option takes precedence, Match + set statements withstanding. + + + + + + + + + + + + + + + + + + + + + Ensure sshd IgnoreRhosts is enabled + + +The IgnoreRhosts + parameter specifies that .rhosts + and .shosts + files will not be used in RhostsRSAAuthentication + or HostbasedAuthentication +. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Setting this parameter forces users to enter a password when authenticating with SSH. + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the IgnoreRhosts + parameter to yes +: + IgnoreRhosts yes + + + Note: + First occurrence of a option takes precedence. + + + + + + + + + + + + + + + + Ensure sshd KexAlgorithms is configured + + Key exchange is any method in cryptography by which cryptographic keys are exchanged between two parties, allowing use of a cryptographic algorithm. If the sender and receiver wish to exchange encrypted messages, each must be equipped to encrypt messages to be sent and decrypt messages received + + Notes: + + + Kex algorithms have a higher preference the earlier they appear in the list + Some organizations may have stricter requirements for approved Key exchange algorithms + Ensure that Key exchange algorithms used are in compliance with site policy + +The only Key Exchange Algorithms currently FIPS 140-2 approved are: + + ecdh-sha2-nistp256 + ecdh-sha2-nistp384 + ecdh-sha2-nistp521 + diffie-hellman-group-exchange-sha256 + diffie-hellman-group16-sha512 + diffie-hellman-group18-sha512 + diffie-hellman-group14-sha256 + + + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + The supported algorithms are: + +curve25519-sha256
+curve25519-sha256@libssh.org
+diffie-hellman-group1-sha1
+diffie-hellman-group14-sha1
+diffie-hellman-group-exchange-sha1
+diffie-hellman-group-exchange-sha256
+ecdh-sha2-nistp256
+ecdh-sha2-nistp384
+ecdh-sha2-nistp521 +
+
+
+ + Key exchange methods that are considered weak should be removed. A key exchange method may be weak because too few bits are used, or the hashing algorithm is considered too weak. Using weak algorithms could expose connections to man-in-the-middle attacks + + + + NIST SP 800-53 Rev. 5: SC-8 + + + + +Edit /etc/ssh/sshd_config + and add/modify the KexAlgorithms + line to contain a comma separated list of the site approved, supported "strong" KexAlgorithms: + + Example: + + KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256 + + + Note: + First occurrence of an option takes precedence. + + + + + + + + + + + + + +
+ + Ensure sshd LoginGraceTime is configured + + +The LoginGraceTime + parameter specifies the time allowed for successful authentication to the SSH server. The longer the Grace period is the more open unauthenticated connections can exist. Like other session controls in this session the Grace Period should be limited to appropriate organizational limits to ensure the service is available for needed access. + + + +Setting the LoginGraceTime + parameter to a low number will minimize the risk of successful brute force attacks to the SSH server. It will also limit the number of concurrent unauthenticated connections While the recommended setting is 60 seconds (1 Minute), set the number based on site policy. + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-6 + + + + +Edit the /etc/ssh/sshd_config + file to set the LoginGraceTime + parameter is 60 + or 1m +: + LoginGraceTime 60 + + + Note: + First occurrence of a option takes precedence. + + + + + + + + + + + + + + + + Ensure sshd LogLevel is configured + + + LogLevel + gives the verbosity level that is used when logging messages from sshd. The possible values are: QUIET +, FATAL +, ERROR +, INFO +, VERBOSE +, DEBUG +, DEBUG1 +, DEBUG2 +, and DEBUG3 +. The default is INFO. DEBUG + and DEBUG1 + are equivalent. DEBUG2 + and DEBUG3 + each specify higher levels of debugging output. + + Note: + Logging with a DEBUG level violates the privacy of users and is not recommended. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + +SSH provides several logging levels with varying amounts of verbosity. The DEBUG + options are specifically not + recommended other than strictly for debugging SSH communications. These levels provide so much data that it is difficult to identify important security information, and may violate the privacy of users. + +The INFO + level is the basic level that only records login activity of SSH users. In many situations, such as Incident Response, it is important to determine when a particular user was active on a system. The logout record can eliminate those users who disconnected, which helps narrow the field. + +The VERBOSE + level specifies that login and logout activity as well as the key fingerprint for any SSH key used for login will be logged. This information is important for SSH key management, especially in legacy environments. + + + + + https://www.ssh.com/ssh/sshd_config/ + NIST SP 800-53 Rev. 5: AU-3, AU-12, SI-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the LogLevel + parameter as follows: + +LogLevel VERBOSE
+ - OR -
+LogLevel INFO +
+ + Note: + First occurrence of a option takes precedence. +
+
+
+ + + + + + + + + + +
+ + Ensure sshd MACs are configured + + This variable limits the types of MAC algorithms that SSH can use during communication. + + Notes: + + + Some organizations may have stricter requirements for approved MACs. + Ensure that MACs used are in compliance with site policy. + +The only "strong" MACs currently FIPS 140-2 approved are: + + HMAC-SHA1 + HMAC-SHA2-256 + HMAC-SHA2-384 + HMAC-SHA2-512 + + + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + Users + Protect + + + + + The supported MACs are: + +hmac-md5
+hmac-md5-96
+hmac-ripemd160
+hmac-sha1
+hmac-sha1-96
+hmac-sha2-256
+hmac-sha2-512
+umac-64@openssh.com
+umac-128@openssh.com
+hmac-md5-etm@openssh.com
+hmac-md5-96-etm@openssh.com
+hmac-ripemd160-etm@openssh.com
+hmac-sha1-etm@openssh.com
+hmac-sha1-96-etm@openssh.com
+hmac-sha2-256-etm@openssh.com
+hmac-sha2-512-etm@openssh.com
+umac-64-etm@openssh.com
+umac-128-etm@openssh.com +
+
+
+ + MD5 and 96-bit MAC algorithms are considered weak and have been shown to increase exploitability in SSH downgrade attacks. Weak algorithms continue to have a great deal of attention as a weak spot that can be exploited with expanded computing power. An attacker that breaks the algorithm could take advantage of a MiTM position to decrypt the SSH tunnel and capture credentials and information. + + + + + More information on SSH downgrade attacks can be found here: http://www.mitls.org/pages/attacks/SLOTH + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file and add/modify the MACs + line to contain a comma separated list of the site approved, supported "strong" MACs: + + Example: + + MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-256 + + + Note: + The first occurrence of an option takes precedence. + + + + + + + + + + + + + +
+ + Ensure sshd MaxAuthTries is configured + + +The MaxAuthTries + parameter specifies the maximum number of authentication attempts permitted per connection. When the login failure count reaches half the number, error messages will be written to the syslog + file detailing the login failure. + + + + + + + Network + Detect + + + + + + Users + Detect + + + + + + +Setting the MaxAuthTries + parameter to a low number will minimize the risk of successful brute force attacks to the SSH server. While the recommended setting is 4, set the number based on site policy. + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: AU-3 + + + + +Edit the /etc/ssh/sshd_config + file to set the MaxAuthTries + parameter to 4 + or less above any Match + entries as follows: + MaxAuthTries 4 + + + Note: + First occurrence of a option takes precedence, Match set statements withstanding. + + + + + + + + + + + + + + + + + + + + + Ensure sshd MaxSessions is configured + + +The MaxSessions + parameter specifies the maximum number of open sessions permitted from a given connection. + + + To protect a system from denial of service due to a large number of concurrent sessions, use the rate limiting function of MaxSessions to protect availability of sshd logins and prevent overwhelming the daemon. + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the MaxSessions + parameter to 10 + or less above any Match + entries as follows: + MaxSessions 10 + + + Note: + First occurrence of a option takes precedence, Match set statements withstanding. + + + + + + + + + + + + + + + + + + + + + Ensure sshd MaxStartups is configured + + +The MaxStartups + parameter specifies the maximum number of concurrent unauthenticated connections to the SSH daemon. + + + + + + + Applications + Protect + + + + + + Applications + Protect + + + + + + To protect a system from denial of service due to a large number of pending authentication connection attempts, use the rate limiting function of MaxStartups to protect availability of sshd logins and prevent overwhelming the daemon. + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the MaxStartups + parameter to 10:30:60 + or more restrictive: + MaxStartups 10:30:60 + + + Note: + First occurrence of a option takes precedence. + + + + + + + + + + + + + + + + Ensure sshd PermitEmptyPasswords is disabled + + +The PermitEmptyPasswords + parameter specifies if the SSH server allows login to accounts with empty password strings. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Disallowing remote shell access to accounts that have an empty password reduces the probability of unauthorized access to the system. + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Edit /etc/ssh/sshd_config + and set the PermitEmptyPasswords + parameter to no + above any Match + entries: + PermitEmptyPasswords no + + + Note: + First occurrence of a option takes precedence, Match + set statements withstanding. + + + + + + + + + + + + + + + + + + + + + Ensure sshd PermitRootLogin is disabled + + +The PermitRootLogin + parameter specifies if the root user can log in using SSH. The default is prohibit-password +. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + +Disallowing root + logins over SSH requires system admins to authenticate using their own individual account, then escalating to root +. This limits opportunity for non-repudiation and provides a clear audit trail in the event of a security incident. + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5:AC-6 + + + + +Edit the /etc/ssh/sshd_config + file to set the PermitRootLogin + parameter to no + above any Match + entries as follows: + PermitRootLogin no + + + Note: + First occurrence of a option takes precedence, Match + set statements withstanding. + + + + + + + + + + + + + + + + + + + + + Ensure sshd PermitUserEnvironment is disabled + + +The PermitUserEnvironment + option allows users to present environment options to the SSH daemon. + + + Permitting users the ability to set environment variables through the SSH daemon could potentially allow users to bypass security controls (e.g. setting an execution path that has SSH executing trojan'd programs) + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1,CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the parameter PermitUserEnvironment + to no +: + PermitUserEnvironment no + + + Note: + First occurrence of a option takes precedence. + + + + + + + + + + + + + + + + Ensure sshd UsePAM is enabled + + +The UsePAM + directive enables the Pluggable Authentication Module (PAM) interface. If set to yes + this will enable PAM authentication using ChallengeResponseAuthentication + and PasswordAuthentication + directives in addition to PAM account and session module processing for all authentication types. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + +When usePAM + is set to yes +, PAM runs through account and session types properly. This is important if you want to restrict access to services based off of IP, time or other factors of the account. Additionally, you can make sure users inherit certain environment variables on login or disallow access to the server + + + + SSHD_CONFIG(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the /etc/ssh/sshd_config + file to set the UsePAM + parameter to yes +: + UsePAM yes + + + Note: + First occurrence of a option takes precedence. + + + + + + + + + + + + + + +
+ + Configure privilege escalation + + There are various tools which allows a permitted user to execute a command as the superuser or another user, as specified by the security policy. + sudo + + https://www.sudo.ws/ + + The invoking user's real (not effective) user ID is used to determine the user name with which to query the security policy. + + sudo + supports a plug-in architecture for security policies and input/output logging. Third parties can develop and distribute their own policy and I/O logging plug-ins to work seamlessly with the sudo + front end. The default security policy is sudoers +, which is configured via the file /etc/sudoers + and any entries in /etc/sudoers.d +. + pkexec + + https://www.freedesktop.org/software/polkit/docs/0.105/pkexec.1.html + + + + Ensure sudo is installed + + + sudo + allows a permitted user to execute a command as the superuser or another user, as specified by the security policy. The invoking user's real (not effective) user ID is used to determine the user name with which to query the security policy. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + + sudo + supports a plug-in architecture for security policies and input/output logging. Third parties can develop and distribute their own policy and I/O logging plug-ins to work seamlessly with the sudo + front end. The default security policy is sudoers +, which is configured via the file /etc/sudoers + and any entries in /etc/sudoers.d +. + +The security policy determines what privileges, if any, a user has to run sudo +. The policy may require that users authenticate themselves with a password or another authentication mechanism. If authentication is required, sudo + will exit if the user's password is not entered within a configurable time limit. This limit is policy-specific. + + + + SUDO(8) + NIST SP 800-53 Rev. 5: AC-6(2), AC-6(5) + + + + Run the following command to install sudo + # yum install sudo + + + + + + + + + + + + Ensure sudo commands use pty + + + sudo + can be configured to run only from a pseudo terminal ( pseudo-pty +). + + + + + + + Users + Protect + + + + + + Applications + Protect + + + + + + +Attackers can run a malicious program using sudo + which would fork a background process that remains even when the main program has finished executing. + + + + SUDO(8) + VISUDO(8) + NIST SP 800-53 Rev. 5: AC-6 + + + + +Edit the file /etc/sudoers + with visudo + or a file in /etc/sudoers.d/ + with visudo -f + < PATH_TO_FILE +> and add the following line: + Defaults use_pty + + + Note: + + + +sudo will read each file in /etc/sudoers.d +, skipping file names that end in ~ + or contain a . + character to avoid causing problems with package manager or editor temporary/backup files. + +Files are parsed in sorted lexical order. That is, /etc/sudoers.d/01_first + will be parsed before /etc/sudoers.d/10_second +. + +Be aware that because the sorting is lexical, not numeric, /etc/sudoers.d/1_whoops + would be loaded after /etc/sudoers.d/10_second +. + Using a consistent number of leading zeroes in the file names can be used to avoid such problems. + + Impact: + + + WARNING: + Editing the sudo + configuration incorrectly can cause sudo + to stop functioning. Always use visudo + to modify sudo + configuration files. + + + + + + + + + + + + + + + Ensure sudo log file exists + + +The Defaults logfile + entry sets the path to the sudo log file. Setting a path turns on logging to a file; negating this option turns it off. By default, sudo logs via syslog. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + visudo edits the sudoers file in a safe fashion, analogous to vipw(8). visudo locks the sudoers file against multiple simultaneous edits, provides basic sanity checks, and checks for parse errors. If the sudoers file is currently being edited you will receive a message to try again later. + + + + Defining a dedicated log file for sudo simplifies auditing of sudo commands and creation of auditd rules for sudo. + + + + SUDO(8) + VISUDO(8) + sudoers(5) + NIST SP 800-53 Rev. 5: AU-3, AU-12 + + + + +Edit the file /etc/sudoers + or a file in /etc/sudoers.d/ + with visudo or visudo -f <PATH TO FILE> and add the following line: + Defaults logfile="<PATH TO CUSTOM LOG FILE>" + + + Example + + Defaults logfile="/var/log/sudo.log" + + + Note: + + + +sudo will read each file in /etc/sudoers.d +, skipping file names that end in ~ + or contain a . + character to avoid causing problems with package manager or editor temporary/backup files. + +Files are parsed in sorted lexical order. That is, /etc/sudoers.d/01_first + will be parsed before /etc/sudoers.d/10_second +. + +Be aware that because the sorting is lexical, not numeric, /etc/sudoers.d/1_whoops + would be loaded after /etc/sudoers.d/10_second +. + Using a consistent number of leading zeroes in the file names can be used to avoid such problems. + + Impact: + + + WARNING: + Editing the sudo + configuration incorrectly can cause sudo + to stop functioning. Always use visudo + to modify sudo + configuration files. + +Creation of additional log files can cause disk space exhaustion if not correctly managed. You should configure logrotate + to manage the sudo log in accordance with your local policy. + + + + + + + + + + + + + + + Ensure users must provide password for escalation + + The operating system must be configured so that users must provide a password for privilege escalation. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Without re-authentication, users may access resources or perform tasks for which they do not have authorization. + When operating systems provide the capability to escalate a functional capability, it is critical the user re-authenticate. + + + + NIST SP 800-53 Rev. 5: AC-6 + + + + +Based on the outcome of the audit procedure, use visudo -f <PATH TO FILE> + to edit the relevant sudoers file. + +Remove any line with occurrences of NOPASSWD + tags in the file. + Impact: + + This will prevent automated processes from being able to elevate privileges. To include Ansible and AWS builds + + + + + + + + + + + + + + + Ensure re-authentication for privilege escalation is not disabled globally + + The operating system must be configured so that users must re-authenticate for privilege escalation. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Without re-authentication, users may access resources or perform tasks for which they do not have authorization. + When operating systems provide the capability to escalate a functional capability, it is critical the user re-authenticate. + + + + NIST SP 800-53 Rev. 5: AC-6 + + + + Configure the operating system to require users to reauthenticate for privilege escalation. + +Based on the outcome of the audit procedure, use visudo -f <PATH TO FILE> + to edit the relevant sudoers file. + +Remove any occurrences of !authenticate + tags in the file(s). + + + + + + + + + + + + + + Ensure sudo authentication timeout is configured correctly + + + sudo + caches used credentials for a default of 5 minutes. This is for ease of use when there are multiple administrative tasks to perform. The timeout can be modified to suit local security policies. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Setting a timeout value reduces the window of opportunity for unauthorized privileged access to another user. + + + + https://www.sudo.ws/man/1.9.0/sudoers.man.html + + + + +If the currently configured timeout is larger than 15 minutes, edit the file listed in the audit section with visudo -f <PATH TO FILE> + and modify the entry timestamp_timeout= + to 15 minutes or less as per your site policy. The value is in minutes. This particular entry may appear on its own, or on the same line as env_reset +. See the following two examples: + Defaults env_reset, timestamp_timeout=15 + + +Defaults timestamp_timeout=15
+Defaults env_reset +
+
+
+
+ + + + + + + + +
+ + Ensure access to the su command is restricted + + +The su + command allows a user to run a command or shell as another user. The program has been superseded by sudo +, which allows for more granular control over privileged access. Normally, the su + command can be executed by any user. By uncommenting the pam_wheel.so + statement in /etc/pam.d/su +, the su + command will only allow users in a specific groups to execute su +. This group should be empty to reinforce the use of sudo + for privileged access. + + + + + + + Data + Protect + + + + + + Applications + Protect + + + + + + +Restricting the use of su + , and using sudo + in its place, provides system administrators better control of the escalation of user privileges to execute privileged commands. The sudo utility also provides a better logging and audit mechanism, as it can log each command executed via sudo + , whereas su + can only record that a user executed the su + program. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Create an empty group that will be specified for use of the su + command. The group should be named according to site policy. + + Example: + + # groupadd sugroup + + +Add the following line to the /etc/pam.d/su + file, specifying the empty group: + auth required pam_wheel.so use_uid group=sugroup + + + + + + + + + + +
+ + Configure Pluggable Authentication Modules + + +Pluggable Authentication Modules (PAM) is a service that implements modular authentication modules on UNIX systems. PAM is implemented as a set of shared objects that are loaded and executed when a program needs to authenticate a user. Files for PAM are typically located in the /etc/pam.d + directory. PAM must be carefully configured to secure system authentication. While this section covers some of PAM, please consult other PAM resources to fully understand the configuration capabilities. + + + Configure PAM software packages + + Updated versions of PAM and authselect include additional functionality + + + Ensure latest version of pam is installed + + Updated versions of PAM include additional functionality + + + To ensure the system has full functionality and access to the options covered by this Benchmark, pam-1.3.1-25 or latter is required + + + + + + - IF - + the version of PAM + on the system is less that version pam-1.1.8-23. +: + +Run the following command to update to the latest version of PAM +: + # yum upgrade pam + + + + + + + + + + + + + + Ensure libpwquality is installed + + The libpwquality package provides common functions for password quality checking + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Strong passwords reduce the risk of systems being hacked through brute force methods. + + + + NIST SP 800-53 Rev. 5: IA-5(1) + + + + +Run the following command to install libpwquality +: + # yum install libpwquality + + + + + + + + + + + + + Configure pluggable module arguments + + Pluggable Authentication Modules (PAM) uses arguments to pass information to a pluggable module during authentication for a particular module type. These arguments allow the PAM configuration files for particular programs to use a common PAM module but in different ways. + +Invalid arguments are ignored and do not otherwise affect the success or failure of the PAM module. When an invalid argument is passed, an error is usually written to /var/log/messages + file. However, since the reporting method is controlled by the PAM module, the module must be written correctly to log the error to this file. + + + Configure pam_faillock module + + + pam_faillock.so + provides a way to configure the default settings for locking the user after multiple failed authentication attempts. + Options: + + + <dir=/path/to/tally-directory> + - The directory where the user files with the failure records are kept. The default is /var/run/faillock. Note: These files will disappear after reboot on systems configured with directory /var/run/faillock mounted on virtual memory. + + audit + - Will log the user name into the system log if the user is not found. + + silent + - Don't print informative messages to the user. Please note that when this option is not used there will be difference in the authentication behavior for users which exist on the system and non-existing users. + + no_log_info + - Don't log informative messages via syslog(3). + + local_users_only + - Only track failed user authentications attempts for local users in /etc/passwd and ignore centralized (AD, IdM, LDAP, etc.) users. The faillock(8) command will also no longer track user failed authentication attempts. Enabling this option will prevent a double-lockout scenario where a user is locked out locally and in the centralized mechanism. + + nodelay + - Don't enforce a delay after authentication failures. + + deny=<n> + - Deny access if the number of consecutive authentication failures for this user during the recent interval exceeds +. The default is 3. + + fail_interval=n + - The length of the interval during which the consecutive authentication failures must happen for the user account lock out is n seconds. The default is 900 (15 minutes). + + unlock_time=n + - The access will be re-enabled after n seconds after the lock out. The value 0 has the same meaning as value never - the access will not be re-enabled without resetting the faillock entries by the faillock(8) command. The default is 600 (10 minutes). Note that the default directory that pam_faillock uses is usually cleared on system boot so the access will be also re-enabled after system reboot. If that is undesirable a different tally directory must be set with the dir option. Also note that it is usually undesirable to permanently lock out users as they can become easily a target of denial of service attack unless the usernames are random and kept secret to potential attackers. + + even_deny_root + - Root account can become locked as well as regular accounts. + + root_unlock_time=n + - This option implies even_deny_root option. Allow access after n seconds to root account after the account is locked. In case the option is not specified the value is the same as of the unlock_time option. + + admin_group=name + - If a group name is specified with this option, members of the group will be handled by this module the same as the root account (the options even_deny_root and root_unlock_time will apply to them. By default the option is not set. + + + + Ensure pam_faillock module is enabled + + +The pam_faillock.so + module maintains a list of failed authentication attempts per user during a specified interval and locks the account in case there were more than the configured number of consecutive failed authentications (this is defined by the deny + parameter in the faillock configuration). It stores the failure records into per-user files in the tally directory. + + + Locking out user IDs after n unsuccessful consecutive login attempts mitigates brute force password attacks against your systems. + + faillock(8) - Linux man page + pam_faillock(8) - Linux man page + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit the files /etc/pam.d/system-auth + and /etc/pam.d/password-auth +: + +Add the following lines to the auth + section: + +auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900 even_deny_root
+auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 even_deny_root +
+ +The auth + sections should look similar to the following example: + + WARNING: + The ordering on the lines in the auth section is important. The preauth + line needs to below the line auth required pam_env.so + and above all password validation lines. The authfail + line needs to be after all password validation lines such as pam_sss.so +. Incorrect order can cause you to be locked out of the system. + + + Example: + + +auth required pam_env.so
+auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900 even_deny_root # <- Under "auth required pam_env.so"
+auth sufficient pam_unix.so try_first_pass
+auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 even_deny_root # <- Last auth line before "auth requisite pam_succeed_if.so"
+auth requisite pam_succeed_if.so uid >= 1000 quiet_success
+auth required pam_deny.so +
+ +Add the following line to the account + section: + account required pam_faillock.so + + + Example: + + +account required pam_faillock.so
+account required pam_unix.so
+account sufficient pam_localuser.so
+account sufficient pam_pam_succeed_if.so uid < 1000 quiet
+account required pam_permit.so +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure password failed attempts lockout is configured + + +The deny=<n> + option will deny access if the number of consecutive authentication failures for this user during the recent interval exceeds +. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + +If a user has been locked out because they have reached the maximum consecutive failure count defined by deny= + in the pam_faillock.so + module, the user can be unlocked by issuing the command faillock --user &amp;lt;USERNAME&amp;amp;gt; --reset +. This command sets the failed count to 0, effectively unlocking the user. + + + + +Locking out user IDs after n + unsuccessful consecutive login attempts mitigates brute force password attacks against your systems. + + + + + + + +Edit the files /etc/pam.d/system-auth + and /etc/pam.d/password-auth +: + +Add the following lines to the auth + section: + +auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900 even_deny_root
+auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 even_deny_root +
+ +The auth + sections should look similar to the following example: + + Example: + + +auth required pam_env.so
+auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900 even_deny_root # <- Under "auth required pam_env.so"
+auth sufficient pam_unix.so try_first_pass
+auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 even_deny_root # <- Last auth line before "auth requisite pam_succeed_if.so"
+auth requisite pam_succeed_if.so uid >= 1000 quiet_success
+auth required pam_deny.so +
+ + WARNING: + The ordering on the lines in the auth section is important. The preauth + line needs to below the line auth required pam_env.so + and above all password validation lines. The authfail + line needs to be after all password validation lines such as pam_sss.so +. Incorrect order can cause you to be locked out of the system. + +
+
+
+ + + + + + + + + + + + + + + + + + +
+ + Ensure password unlock time is configured + + + unlock_time=<n> + - The access will be re-enabled after + seconds after the lock out. The value 0 + has the same meaning as value never - the access will not be re-enabled without resetting the faillock entries by the faillock(8) command. + + Note: + + + The default directory that pam_faillock uses is usually cleared on system boot so the access will be also re-enabled after system reboot. If that is undesirable a different tally directory must be set with the dir option. + It is usually undesirable to permanently lock out users as they can become easily a target of denial of service attack unless the usernames are random and kept secret to potential attackers. + +The maximum configurable value for unlock_time + is 604800 + + + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + +If a user has been locked out because they have reached the maximum consecutive failure count defined by deny= + in the pam_faillock.so + module, the user can be unlocked by issuing the command faillock --user &amp;lt;USERNAME&amp;amp;gt; --reset +. This command sets the failed count to 0, effectively unlocking the user. + + + + +Locking out user IDs after n + unsuccessful consecutive login attempts mitigates brute force password attacks against your systems. + + + + + + + +Edit the files /etc/pam.d/system-auth + and /etc/pam.d/password-auth +: + +Edit the files /etc/pam.d/system-auth + and /etc/pam.d/password-auth +: + +Add the following lines to the auth + section: + +auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900 even_deny_root
+auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 even_deny_root +
+ +The auth + sections should look similar to the following example: + + Example: + + +auth required pam_env.so
+auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900 even_deny_root # <- Under "auth required pam_env.so"
+auth sufficient pam_unix.so try_first_pass
+auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 even_deny_root # <- Last auth line before "auth requisite pam_succeed_if.so"
+auth requisite pam_succeed_if.so uid >= 1000 quiet_success
+auth required pam_deny.so +
+ + WARNING: + The ordering on the lines in the auth section is important. The preauth + line needs to below the line auth required pam_env.so + and above all password validation lines. The authfail + line needs to be after all password validation lines such as pam_sss.so +. Incorrect order can cause you to be locked out of the system. + + Impact: + + +Use of unlock_time=0 + may allow an attacker to cause denial of service to legitimate users. This will also require a systems administrator with elevated privileges to unlock the account. + +
+
+
+ + + + + + + + + + + + + + + + + + +
+ + Ensure password failed attempts lockout includes root account + + + even_deny_root + - Root account can become locked as well as regular accounts + + root_unlock_time=n + - This option implies even_deny_root option. Allow access after n seconds to root account after the account is locked. In case the option is not specified the value is the same as of the unlock_time option. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + +If a user has been locked out because they have reached the maximum consecutive failure count defined by deny= + in the pam_faillock.so + module, the user can be unlocked by issuing the command faillock --user &amp;lt;USERNAME&amp;amp;gt; --reset +. This command sets the failed count to 0, effectively unlocking the user. + + + + Locking out user IDs after n unsuccessful consecutive login attempts mitigates brute force password attacks against your systems. + + + + + + + +Edit the files /etc/pam.d/system-auth + and /etc/pam.d/password-auth +: + +Add the following lines to the auth + section: + +auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900 even_deny_root
+auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 even_deny_root +
+ +The auth + sections should look similar to the following example: + + Example: + + +auth required pam_env.so
+auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900 even_deny_root # <- Under "auth required pam_env.so"
+auth sufficient pam_unix.so try_first_pass
+auth [default=die] pam_faillock.so authfail audit deny=5 unlock_time=900 even_deny_root # <- Last auth line before "auth requisite pam_succeed_if.so"
+auth requisite pam_succeed_if.so uid >= 1000 quiet_success
+auth required pam_deny.so +
+ + WARNING: + The ordering on the lines in the auth section is important. The preauth + line needs to below the line auth required pam_env.so + and above all password validation lines. The authfail + line needs to be after all password validation lines such as pam_sss.so +. Incorrect order can cause you to be locked out of the system. + + Impact: + + +Use of unlock_time=0 + or root_unlock_time=0 + may allow an attacker to cause denial of service to legitimate users. + +
+
+
+ + + + + + + + + + + + + + + + + + +
+
+ + Configure pam_pwquality module + + +The pam_pwquality.so + module checks the strength of passwords. It performs checks such as making sure a password is not a dictionary word, it is a certain length, contains a mix of characters (e.g. alphabet, numeric, other) and more. + These checks are configurable by either: + + use of the module arguments + +modifying the /etc/security/pwquality.conf + configuration file + + + Note: + The module arguments override the settings in the /etc/security/pwquality.conf + configuration file. + + + Ensure pam_pwquality module is enabled + + +The pam_pwquality.so + module performs password quality checking. This module can be plugged into the password stack of a given service to provide strength-checking for passwords. The code was originally based on pam_cracklib module and the module is backwards compatible with its options. + The action of this module is to prompt the user for a password and check its strength against a system dictionary and a set of rules for identifying poor choices. + The first action is to prompt for a single password, check its strength and then, if it is considered strong, prompt for the password a second time (to verify that it was typed correctly on the first occasion). All being well, the password is passed on to subsequent modules to be installed as the new authentication token. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Use of a unique, complex passwords helps to increase the time and resources required to compromise the password. + + + + NIST SP 800-53 Rev. 5: IA-5(1) + + + + Edit the files /etc/pam.d/system-auth and /etc/pam.d/password-auth: + +Add the following line to the password + section: + password requisite pam_pwquality.so try_first_pass local_users_only + + + Example password section: + + +password requisite pam_pwquality.so try_first_pass local_users_only retry=3 #<- added pam_pwquality.so line
+password required pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok
+password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok
+password required pam_deny.so +
+ + Note: + the use_authtok + option should exist on all password lines except the first entry and the pam_deny.so + line +
+
+
+ + + + + + + + +
+ + Ensure password number of changed characters is configured + + +The pwquality + difok + option sets the number of characters in a password that must not be present in the old password. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. + Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Edit or add the following line in /etc/security/pwquality.conf + to a value of 2 + or more and meets local site policy: + difok = 2 + + + Example: + + +# sed -ri 's/^\s*difok\s*=/# &/' /etc/security/pwquality.conf
+# printf '\n%s' "difok = 2" >> /etc/security/pwquality.conf +
+ +Run the following script to remove setting difok + on the pam_pwquality.so + module in the PAM files: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+difok\s*=\s*\S+)(.*$)/\1\4/' /etc/pam.d/"$l_pam_file"
+ done
+} +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure password length is configured + + + minlen + - Minimum acceptable size for the new password (plus one if credits are not disabled which is the default). Cannot be set to lower value than 6. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Strong passwords protect systems from being hacked through brute force methods. + + + + pam_pwquality(8) + NIST SP 800-53 Rev. 5: IA-5 + + + + +Edit the file /etc/security/pwquality.conf + and add or modify the following line to set password length of 14 + or more characters. Ensure that password length conforms to local site policy: + minlen = 14 + + +Run the following script to remove setting minlen + on the pam_pwquality.so + module in the PAM files: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+minlen\s*=\s*[0-9]+)(.*$)/\1\4/' /etc/pam.d/"$l_pam_file"
+ done
+} +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure password complexity is configured + + Password complexity can be set through: + + + minclass + - The minimum number of classes of characters required in a new password. (digits, uppercase, lowercase, others). e.g. minclass = 4 + requires digits, uppercase, lower case, and special characters. + + dcredit + - The maximum credit for having digits in the new password. If less than 0 + it is the minimum number of digits in the new password. e.g. dcredit = -1 + requires at least one digit + + ucredit + - The maximum credit for having uppercase characters in the new password. If less than 0 it is the minimum number of uppercase characters in the new password. e.g. ucredit = -1 + requires at least one uppercase character + + ocredit + - The maximum credit for having other characters in the new password. If less than 0 it is the minimum number of other characters in the new password. e.g. ocredit = -1 + requires at least one special character + + lcredit + - The maximum credit for having lowercase characters in the new password. If less than 0 it is the minimum number of lowercase characters in the new password. e.g. lcredit = -1 + requires at least one lowercase character + + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Strong passwords protect systems from being hacked through brute force methods. + + + + pam_pwquality(8) + PWQUALITY.CONF(5) + NIST SP 800-53 Rev. 5: IA-5 + + + + +Edit /etc/security/pwquality.conf + and add or modify the following line to set: + minclass = 4 + + + --AND/OR-- + + +dcredit = -_N>
+ucredit = <N>
+ocredit = <N>
+lcredit = <N> +
+ + Example: + + # printf '\n%s' "minclass = 4" >> /etc/security/pwquality.conf + + + --AND/OR-- + + # printf '%s\n' "dcredit = -1" "ucredit = -1" "ocredit = -1" "lcredit = -1" >> /etc/security/pwquality.conf + + +Run the following script to remove setting minclass +, dcredit +, ucredit +, lcredit +, and ocredit + on the pam_pwquality.so + module in the PAM files + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+minclass\s*=\s*\S+)(.*$)/\1\4/' /etc/pam.d/"$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+dcredit\s*=\s*\S+)(.*$)/\1\4/' /etc/pam.d/"$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+ucredit\s*=\s*\S+)(.*$)/\1\4/' /etc/pam.d/"$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+lcredit\s*=\s*\S+)(.*$)/\1\4/' /etc/pam.d/"$l_pam_file"
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+ocredit\s*=\s*\S+)(.*$)/\1\4/' /etc/pam.d/"$l_pam_file"
+ done
+} +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure password same consecutive characters is configured + + +The pwquality + maxrepeat + option sets the maximum number of allowed same consecutive characters in a new password. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. + Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Edit /etc/security/pwquality.conf + and add or modify the following line to set maxrepeat + to 3 + or less and not 0 +. Ensure setting conforms to local site policy: + maxrepeat = 3 + + +Run the following script to remove setting maxrepeat + on the pam_pwquality.so + module in the PAM files: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+maxrepeat\s*=\s*\S+)(.*$)/\1\4/' /etc/pam.d/"$l_pam_file"
+ done
+} +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure password maximum sequential characters is configured + + +The pwquality + maxsequence + option sets the maximum length of monotonic character sequences in the new password. Examples of such sequence are 12345 + or fedcb +. The check is disabled if the value is 0 +. + + Note: + Most such passwords will not pass the simplicity check unless the sequence is only a minor part of the password. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Use of a complex password helps to increase the time and resources required to compromise the password. Password complexity, or strength, is a measure of the effectiveness of a password in resisting attempts at guessing and brute-force attacks. + Password complexity is one factor of several that determines how long it takes to crack a password. The more complex the password, the greater the number of possible combinations that need to be tested before the password is compromised. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Edit /etc/security/pwquality.conf + and add or modify the following line to set maxsequence + to 3 + or less and not 0 +. Ensure setting conforms to local site policy: + maxsequence = 3 + + +Run the following script to remove setting maxsequence + on the pam_pwquality.so + module in the PAM files: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+maxsequence\s*=\s*\S+)(.*$)/\1\4/' /etc/pam.d/"$l_pam_file"
+ done
+} +
+
+
+
+ + + + + + + + + + + + + +
+ + Ensure password dictionary check is enabled + + +The pwquality + dictcheck + option sets whether to check for the words from the cracklib + dictionary. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + If the operating system allows the user to select passwords based on dictionary words, this increases the chances of password compromise by increasing the opportunity for successful guesses, and brute-force attacks. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +Edit /etc/security/pwquality.conf + and comment out or remove any instance of dictcheck = 0 +: + + Example: + + # sed -ri 's/^\s*dictcheck\s*=/# &/' /etc/security/pwquality.conf + + +Run the following script to remove setting dictcheck + on the pam_pwquality.so + module in the PAM files: + +#!/usr/bin/env bash
+
+{
+ for l_pam_file in system-auth password-auth; do
+ sed -ri 's/(^\s*password\s+(requisite|required|sufficient)\s+pam_pwquality\.so.*)(\s+dictcheck\s*=\s*\S+)(.*$)/\1\4/' /etc/pam.d/"$l_pam_file"
+ done
+} +
+
+
+
+ + + + + + + + + + + + + +
+
+ + Configure pam_pwhistory module + + + pam_pwhistory + - PAM module to remember last passwords + + pam_history.so + module - This module saves the last passwords for each user in order to force password change history and keep the user from alternating between the same password too frequently. + +This module does not work together with kerberos. In general, it does not make much sense to use this module in conjunction with NIS + or LDAP +, since the old passwords are stored on the local machine and are not available on another machine for password history checking. + Options: + + + debug + - Turns on debugging via syslog(3). + + use_authtok + - When password changing enforce the module to use the new password provided by a previously stacked password module (this is used in the example of the stacking of the pam_passwdqc module + documented below). + + enforce_for_root + - If this option is set, the check is enforced for root, too. + + remember=<N> + - The last <N> + passwords for each user are saved. The default is 10 +. Value of 0 + makes the module to keep the existing contents of the opasswd file unchanged. + + retry=<N> + - Prompt user at most <N> + times before returning with error. The default is 1 +. + + authtok_type=<STRING> + - See pam_get_authtok(3) for more details. + + The options for configuring the module behavior are described in the pwhistory.conf(5) manual page. + + + Ensure pam_pwhistory module is enabled + + +The pam_history.so + module saves the last passwords for each user in order to force password change history and keep the user from alternating between the same password too frequently. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Requiring users not to reuse their passwords make it less likely that an attacker will be able to guess the password or use a compromised password. + + + + NIST SP 800-53 Rev. 5: IA-5(1) + + + + Edit the files /etc/pam.d/system-auth and /etc/pam.d/password-auth: + +Add the following line to the password + section: + password required pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok + + + Example password section: + + +password requisite pam_pwquality.so try_first_pass local_users_only retry=3
+password required pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok
+password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok
+password required pam_deny.so +
+ + Note: + the use_authtok + option should exist on all password lines except the first entry and the pam_deny.so + line +
+
+
+ + + + + + + + +
+ + Ensure password history remember is configured + + +The /etc/security/opasswd + file stores the users' old passwords and can be checked to ensure that users are not recycling recent passwords. + + +remember=<N> - <N> + is the number of old passwords to remember + + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Requiring users not to reuse their passwords make it less likely that an attacker will be able to guess the password or use a compromised password. + + Note: + These change only apply to accounts configured on the local system. + + + + NIST SP 800-53 Rev. 5: IA-5(1) + + + + Edit the files /etc/pam.d/system-auth and /etc/pam.d/password-auth: + +Add the following line to the password + section: + password required pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok + + + Example password section: + + +password requisite pam_pwquality.so try_first_pass local_users_only retry=3
+password required pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok
+password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok
+password required pam_deny.so +
+ + Note: + the use_authtok + option should exist on all password lines except the first entry and the pam_deny.so + line +
+
+
+ + + + + + + + +
+ + Ensure password history is enforced for the root user + + +If the pwhistory + enforce_for_root + option is enabled, the module will enforce password history for the root user as well + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Requiring users not to reuse their passwords make it less likely that an attacker will be able to guess the password or use a compromised password + + Note: + These change only apply to accounts configured on the local system. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + Edit the files /etc/pam.d/system-auth and /etc/pam.d/password-auth: + +Add the following line to the password + section: + password required pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok + + + Example password section: + + +password requisite pam_pwquality.so try_first_pass local_users_only retry=3 #<- added pam_pwquality.so line
+password required pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok
+password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok
+password required pam_deny.so +
+ + Note: + the use_authtok + option should exist on all password lines except the first entry and the pam_deny.so + line +
+
+
+ + + + + + + + +
+ + Ensure pam_pwhistory includes use_authtok + + + use_authtok + - When password changing enforce the module to set the new password to the one provided by a previously stacked password module + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + + use_authtok + allows multiple pam modules to confirm a new password before it is accepted. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + Edit the files /etc/pam.d/system-auth and /etc/pam.d/password-auth: + +Add the following line to the password + section: + password required pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok + + + Example password section: + + +password requisite pam_pwquality.so try_first_pass local_users_only retry=3 #<- added pam_pwquality.so line
+password required pam_pwhistory.so remember=24 enforce_for_root try_first_pass use_authtok
+password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok
+password required pam_deny.so +
+ + Note: + the use_authtok + option should exist on all password lines except the first entry and the pam_deny.so + line +
+
+
+ + + + + + + + +
+
+ + Configure pam_unix module + + +The pam_unix.so + module is the standard Unix authentication module. It uses standard calls from the system's libraries to retrieve and set account information as well as authentication. Usually this is obtained from the /etc/passwd + and the /etc/shadow + file as well if shadow is enabled. + + + Ensure pam_unix does not include nullok + + +The nullok + argument overrides the default action of pam_unix.so + to not permit the user access to a service if their official password is blank. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + Using a strong password is essential to helping protect personal and sensitive information from unauthorized access + + + + + + + Edit the files /etc/pam.d/system-auth and /etc/pam.d/password-auth: + +Edit the following lines and remove the nullok + option: + +auth sufficient pam_unix.so try_first_pass
+account required pam_unix.so
+password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok
+session required pam_unix.so +
+
+
+
+ + + + + + + + +
+ + Ensure pam_unix does not include remember + + +The remember=n + argument saves the last n passwords for each user in /etc/security/opasswd + in order to force password change history and keep the user from alternating between the same password too frequently. The MD5 password hash algorithm is used for storing the old passwords. Instead of this option the pam_pwhistory + module should be used. The pam_pwhistory + module saves the last n passwords for each user in /etc/security/opasswd + using the password hash algorithm set on the pam_unix + module. This allows for the sha512 + hash algorithm to be used. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + +The remember=n + argument should be removed to ensure a strong password hashing algorithm is being used. A stronger hash provides additional protection to the system by increasing the level of effort needed for an attacker to successfully determine local user's old passwords stored in /etc/security/opasswd +. + + + + + + + Edit the files /etc/pam.d/system-auth and /etc/pam.d/password-auth: + +Edit the following lines and remove the remember= + option: + +auth sufficient pam_unix.so try_first_pass
+account required pam_unix.so
+password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok
+session required pam_unix.so +
+
+
+
+ + + + + + + + +
+ + Ensure pam_unix includes a strong password hashing algorithm + + A cryptographic hash function converts an arbitrary-length input into a fixed length output. Password hashing performs a one-way transformation of a password, turning the password into another string, called the hashed password. + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + Additional module options may be set, recommendation only covers those listed here. + The following command may be used to expire all non-system user ID's immediately and force them to change their passwords on next login. Any system accounts that need to be expired should be carefully done separately by the system administrator to prevent any potential problems. + # awk -F: '( $3&amp;amp;lt;'"$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)"' &amp;amp;&amp;amp; $1 != "nfsnobody" ) { print $1 }' /etc/passwd | xargs -n 1 chage -d 0 + + + + + +The SHA-512 + algorithm provides a stronger hash than other algorithms used by Linux for password hash generation. A stronger hash provides additional protection to the system by increasing the level of effort needed for an attacker to successfully determine local user passwords. + + Note: + These changes only apply to the local system. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + + Note: + This only effects local users and passwords created after updating the files to use sha512 +. If it is determined that the password algorithm being used is not sha512 +, once it is changed, it is recommended that all user ID's be immediately expired and forced to change their passwords on next login. + Edit the files /etc/pam.d/system-auth and /etc/pam.d/password-auth: + Edit the following lines and: + + +Add the sha512 + argument + +Remove all md5 +, bigcrypt +, sha256 +, and blowfish + arguments + + +auth sufficient pam_unix.so try_first_pass
+account required pam_unix.so
+password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok
+session required pam_unix.so +
+
+
+
+ + + + + + + + +
+ + Ensure pam_unix includes use_authtok + + + use_authtok + - When password changing enforce the module to set the new password to the one provided by a previously stacked password module + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + + use_authtok + allows multiple pam modules to confirm a new password before it is accepted. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + Edit the files /etc/pam.d/system-auth and /etc/pam.d/password-auth: + +Edit the following line and add the use_authtok + argument: + password sufficient pam_unix.so sha512 shadow try_first_pass use_authtok + + + + + + + + + + + + + +
+
+
+ + User Accounts and Environment + + This section provides guidance on setting up secure defaults for system and user accounts and their environment. + + + Configure shadow password suite parameters + + +While a majority of the password control parameters have been moved to PAM, some parameters are still available through the shadow password suite. Any changes made to /etc/login.defs +will only be applied if the usermod +command is used. If user IDs are added a different way, use the chage +command to effect changes to individual user IDs. + + + Ensure strong password hashing algorithm is configured + + A cryptographic hash function converts an arbitrary-length input into a fixed length output. Password hashing performs a one-way transformation of a password, turning the password into another string, called the hashed password. + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + Additional module options may be set, recommendation only covers those listed here. + The following command may be used to expire all non-system user ID's immediately and force them to change their passwords on next login. Any system accounts that need to be expired should be carefully done separately by the system administrator to prevent any potential problems. + # awk -F: '( $3&amp;amp;lt;'"$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)"' &amp;amp;&amp;amp; $1 != "nfsnobody" ) { print $1 }' /etc/passwd | xargs -n 1 chage -d 0 + + + + + +The SHA-512 + and yescrypt + algorithms provide a stronger hash than other algorithms used by Linux for password hash generation. A stronger hash provides additional protection to the system by increasing the level of effort needed for an attacker to successfully determine local user passwords. + + Note: + These changes only apply to the local system. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + + Note: + If yescrypt + becomes available in a future release, this would also be acceptable. It is highly recommended that the chosen hashing algorithm is consistent across /etc/libuser.conf +, /etc/login.defs +, /etc/pam.d/password-auth +, and /etc/pam.d/system-auth +. + Set password hashing algorithm to sha512. + +Edit /etc/libuser.conf + and edit or add the following line: + crypt_style = sha512 + + +Edit /etc/login.defs + and edit or add the following line: + ENCRYPT_METHOD SHA512 + + + Note: + This only effects local users and passwords created after updating the files to use sha512 + or yescrypt +. If it is determined that the password algorithm being used is not sha512 + or yescrypt +, once it is changed, it is recommended that all group passwords be updated to use the stronger hashing algorithm. + + + + + + + + + + + + + + Ensure password expiration is 365 days or less + + +The PASS_MAX_DAYS + parameter in /etc/login.defs + allows an administrator to force passwords to expire once they reach a defined age. It is recommended that the PASS_MAX_DAYS + parameter be set to less than or equal to 365 days. + + + + + + + Applications + Protect + + + + + + Users + Protect + + + + + A value of -1 will disable password expiration. + + + + The window of opportunity for an attacker to leverage compromised credentials or successfully compromise credentials via an online brute force attack is limited by the age of the password. Therefore, reducing the maximum age of a password also reduces an attacker's window of opportunity. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Set the PASS_MAX_DAYS + parameter to conform to site policy in /etc/login.defs + : + PASS_MAX_DAYS 365 + + Modify user parameters for all users with a password set to match: + # chage --maxdays 365 <user> + + Impact: + + The password expiration must be greater than the minimum days between password changes or users will be unable to change their password + + + + + + + + + + + + + + + + + + + + + + Ensure password expiration warning days is 7 or more + + +The PASS_WARN_AGE + parameter in /etc/login.defs + allows an administrator to notify users that their password will expire in a defined number of days. It is recommended that the PASS_WARN_AGE + parameter be set to 7 or more days. + + + + + + + Applications + Protect + + + + + + Users + Protect + + + + + + Providing an advance warning that a password will be expiring gives users time to think of a secure password. Users caught unaware may choose a simple password or write it down where it may be discovered. + + + + + + + +Set the PASS_WARN_AGE + parameter to 7 in /etc/login.defs + : + PASS_WARN_AGE 7 + + Modify user parameters for all users with a password set to match: + # chage --warndays 7 <user> + + + + + + + + + + + + + + + + Ensure inactive password lock is 30 days or less + + User accounts that have been inactive for over a given period of time can be automatically disabled. It is recommended that accounts that are inactive for 30 days after password expiration be disabled. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + A value of -1 would disable this setting. + + + + Inactive accounts pose a threat to system security since the users are not logging in to notice failed login attempts or other anomalies. + + + + + + + Run the following command to set the default password inactivity period to 30 days: + # useradd -D -f 30 + + Modify user parameters for all users with a password set to match: + # chage --inactive 30 <user> + + + + + + + + + + + + + + + + + + + + + + Ensure all users last password change date is in the past + + All users should have a password change date in the past. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + If a user's recorded password change date is in the future, then they could bypass any set password expiration. + + + + + + + Investigate any users with a password change date in the future and correct them. Locking the account, expiring the password, or resetting the password manually may be appropriate. + + + + + + + + + + + + + Configure root and system accounts and environment + + + Ensure default group for the root account is GID 0 + + +The usermod + command can be used to specify which group the root + account belongs to. This affects permissions of files that are created by the root + account. + + + + + + + Applications + Protect + + + + + + Applications + Protect + + + + + + +Using GID 0 for the root + account helps prevent root + -owned files from accidentally becoming accessible to non-privileged users. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Run the following command to set the root + user's default group ID to 0 +: + # usermod -g 0 root + + + + + + + + + + + + + + Ensure root user umask is configured + + +The user file-creation mode mask ( umask +) is used to determine the file permission for newly created directories and files. In Linux, the default permissions for any newly created directory is 0777 ( rwxrwxrwx +), and for any newly created file it is 0666 ( rw-rw-rw- +). The umask + modifies the default Linux permissions by restricting (masking) these permissions. The umask + is not simply subtracted, but is processed bitwise. Bits set in the umask + are cleared in the resulting file mode. + + umask + can be set with either Octal + or Symbolic + values: + + + Octal + (Numeric) Value - Represented by either three or four digits. ie umask 0027 + or umask 027 +. If a four digit umask is used, the first digit is ignored. The remaining three digits effect the resulting permissions for user, group, and world/other respectively. + + Symbolic + Value - Represented by a comma separated list for User u +, group g +, and world/other o +. The permissions listed are not masked by umask +. ie a umask + set by umask u=rwx,g=rx,o= + is the Symbolic + equivalent of the Octal + umask 027 +. This umask + would set a newly created directory with file mode drwxr-x--- + and a newly created file with file mode rw-r----- +. + + + root user Shell Configuration Files: + + + + /root/.bash_profile + - Is executed to configure the root users' shell before the initial command prompt. Is only read by login shells. + + + /root/.bashrc + - Is executed for interactive shells. only read by a shell that's both interactive and non-login + + + + umask + is set by order of precedence. If umask + is set in multiple locations, this order of precedence will determine the system's default umask +. + + Order of precedence: + + + + /root/.bash_profile + + + /root/.bashrc + + The system default umask + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Setting a secure value for umask + ensures that users make a conscious choice about their file permissions. A permissive umask + value could result in directories or files with excessive permissions that can be read and/or written to by unauthorized users. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Edit /root/.bash_profile + and /root/.bashrc + and remove, comment out, or update any line with umask + to be 0027 + or more restrictive. + + + + + + + + + + + + + + Ensure system accounts are secured + + There are a number of accounts provided with most distributions that are used to manage applications and are not intended to provide an interactive shell. Furthermore, a user may add special accounts that are not intended to provide an interactive shell. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + +The root +, sync +, shutdown +, and halt + users are exempted from requiring a non-login shell. + + + + +It is important to make sure that accounts that are not being used by regular users are prevented from being used to provide an interactive shell. By default, most distributions set the password field for these accounts to an invalid string, but it is also recommended that the shell field in the password file be set to the nologin + shell. This prevents the account from potentially being used to run any commands. + + + + NIST SP 800-53 Rev. 5: AC-2(5), AC-3, AC-11, MP-2 + + + + System accounts + +Set the shell for any accounts returned by the audit to nologin +: + # usermod -s $(command -v nologin) <user> + + Disabled accounts + Lock any non root accounts returned by the audit: + # usermod -L <user> + + Large scale changes + +The following command will set all system accounts to nologin +: + # awk -F: '($1!~/^(root|halt|sync|shutdown|nfsnobody)$/ && ($3<'"$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)"' || $3 == 65534)) { print $1 }' /etc/passwd | while read user; do usermod -s $(command -v nologin) $user >/dev/null; done + + +The following command will automatically lock all accounts that have their shell set to nologin +: + # awk -F: '/nologin/ {print $1}' /etc/passwd | while read user; do usermod -L $user; done + + + + + + + + + + + + + + + + + Ensure root password is set + + There are a number of methods to access the root account directly. Without a password set any user would be able to gain access and thus control over the entire system. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +Access to root + should be secured at all times. + + + + + + + +Set the root + password with: + # passwd root + + Impact: + + If there are any automated processes that relies on access to the root account without authentication, they will fail after remediation. + + + + + + + + + + + + + + Configure user default environment + + + Ensure nologin is not listed in /etc/shells + + + /etc/shells + is a text file which contains the full pathnames of valid login shells. This file is consulted by chsh + and available to be queried by other programs. + Be aware that there are programs which consult this file to find out if a user is a normal user; for example, FTP daemons traditionally disallow access to users with shells not included in this file. + + + +A user can use chsh + to change their configured shell. + +If a user has a shell configured that isn't in in /etc/shells +, then the system assumes that they're somehow restricted. In the case of chsh + it means that the user cannot change that value. + Other programs might query that list and apply similar restrictions. + +By putting nologin + in /etc/shells +, any user that has nologin + as its shell is considered a full, unrestricted user. This is not the expected behavior for nologin +. + + shells(5) + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Edit /etc/shells + and remove any lines that include nologin + + + + + + + + + + + + Ensure default user shell timeout is configured + + + TMOUT + is an environmental setting that determines the timeout of a shell in seconds. + + +TMOUT= n + - Sets the shell timeout to n + seconds. A setting of TMOUT=0 + disables timeout. + readonly TMOUT- Sets the TMOUT environmental variable as readonly, preventing unwanted modification during run-time. + export TMOUT - exports the TMOUT variable + + + System Wide Shell Configuration Files: + + + + /etc/profile + - used to set system wide environmental variables on users shells. The variables are sometimes the same ones that are in the .bash_profile +, however this file is used to set an initial PATH or PS1 for all shell users of the system. +is only executed for interactive login + shells, or shells executed with the --login parameter. + + + /etc/profile.d + - /etc/profile + will execute the scripts within /etc/profile.d/*.sh +. It is recommended to place your configuration in a shell script within /etc/profile.d + to set your own system wide environmental variables. + + /etc/bashrc + - System wide version of .bashrc +. In Fedora derived distributions, /etc/bashrc + also invokes /etc/profile.d/*.sh if non-login + shell, but redirects output to /dev/null + if non-interactive. + +Is only executed for interactive + shells or if BASH_ENV + is set to /etc/bashrc +. + + + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + The audit and remediation in this recommendation apply to bash and shell. If other shells are supported on the system, it is recommended that their configuration files also are checked. Other methods of setting a timeout exist for other shells not covered here. + Ensure that the timeout conforms to your local policy. + + + + Setting a timeout value reduces the window of opportunity for unauthorized user access to another user's shell session that has been left unattended. It also ends the inactive session and releases the resources associated with that session. + + + + + + + +Review /etc/bashrc +, /etc/profile +, and all files ending in *.sh + in the /etc/profile.d/ + directory and remove or edit all TMOUT=_n_ + entries to follow local site policy. TMOUT + should not exceed 900 or be equal to 0 +. + +Configure TMOUT + in one + of the following files: + + +A file in the /etc/profile.d/ + directory ending in .sh + + + /etc/profile + + + /etc/bashrc + + + + + TMOUT + configuration examples: + + + As multiple lines: + + +TMOUT=900
+readonly TMOUT
+export TMOUT +
+ + As a single line: + + readonly TMOUT=900 ; export TMOUT + +
+
+
+ + + + + + +
+ + Ensure default user umask is configured + + +The user file-creation mode mask ( umask +) is used to determine the file permission for newly created directories and files. In Linux, the default permissions for any newly created directory is 0777 ( rwxrwxrwx +), and for any newly created file it is 0666 ( rw-rw-rw- +). The umask + modifies the default Linux permissions by restricting (masking) these permissions. The umask + is not simply subtracted, but is processed bitwise. Bits set in the umask + are cleared in the resulting file mode. + + umask + can be set with either Octal + or Symbolic + values: + + + Octal + (Numeric) Value - Represented by either three or four digits. ie umask 0027 + or umask 027 +. If a four digit umask is used, the first digit is ignored. The remaining three digits effect the resulting permissions for user, group, and world/other respectively. + + Symbolic + Value - Represented by a comma separated list for User u +, group g +, and world/other o +. The permissions listed are not masked by umask +. ie a umask + set by umask u=rwx,g=rx,o= + is the Symbolic + equivalent of the Octal + umask 027 +. This umask + would set a newly created directory with file mode drwxr-x--- + and a newly created file with file mode rw-r----- +. + + +The default umask + can be set to use the pam_umask + module or in a System Wide Shell Configuration File +. The user creating the directories or files has the discretion of changing the permissions via the chmod command, or choosing a different default umask + by adding the umask + command into a User Shell Configuration File +, ( .bash_profile + or .bashrc +), in their home directory. + + Setting the default umask: + + + +pam_umask module: + + +will set the umask according to the system default in /etc/login.defs + and user settings, solving the problem of different umask + settings with different shells, display managers, remote sessions etc. + + umask=<mask> + value in the /etc/login.defs + file is interpreted as Octal + +Setting USERGROUPS_ENAB + to yes in /etc/login.defs + (default): + + +will enable setting of the umask + group bits to be the same as owner bits. (examples: 022 -> 002, 077 -> 007) for non-root users, if the uid + is the same as gid +, and username + is the same as the <primary group name> + + userdel will remove the user's group if it contains no more members, and useradd will create by default a group with the name of the user + + + + + + System Wide Shell Configuration File +: + + + /etc/profile + - used to set system wide environmental variables on users shells. The variables are sometimes the same ones that are in the .bash_profile +, however this file is used to set an initial PATH or PS1 for all shell users of the system. +is only executed for interactive login + shells, or shells executed with the --login parameter. + + + /etc/profile.d + - /etc/profile + will execute the scripts within /etc/profile.d/*.sh +. It is recommended to place your configuration in a shell script within /etc/profile.d + to set your own system wide environmental variables. + + /etc/bashrc + - System wide version of .bashrc +. In Fedora derived distributions, etc/bashrc + also invokes /etc/profile.d/*.sh if non-login + shell, but redirects output to /dev/null + if non-interactive. + +Is only executed for interactive + shells or if BASH_ENV + is set to /etc/bashrc +. + + + + + + User Shell Configuration Files: + + + + ~/.bash_profile + - Is executed to configure your shell before the initial command prompt. Is only read by login shells. + + + ~/.bashrc + - Is executed for interactive shells. only read by a shell that's both interactive and non-login + + + + umask + is set by order of precedence. If umask + is set in multiple locations, this order of precedence will determine the system's default umask +. + + Order of precedence: + + + +A file in /etc/profile.d/ + ending in .sh + - This will override any other system-wide umask + setting + +In the file /etc/profile + + +On the pam_umask.so + module in /etc/pam.d/postlogin + + +In the file /etc/login.defs + + +In the file /etc/default/login + + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Other methods of setting a default user umask exist + If other methods are in use in your environment they should be audited + The default user umask can be overridden with a user specific umask + +The user creating the directories or files has the discretion of changing the permissions: + + Using the chmod command + Setting a different default umask by adding the umask command into a User Shell Configuration File, (.bashrc), in their home directory + Manually changing the umask for the duration of a login session by running the umask command + + + + + + + +Setting a secure default value for umask + ensures that users make a conscious choice about their file permissions. A permissive umask + value could result in directories or files with excessive permissions that can be read and/or written to by unauthorized users. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + Run the following script and perform the instructions in the output: + +#!/usr/bin/env bash
+
+{
+ l_output="" l_output2="" l_out=""
+ file_umask_chk()
+ {
+ if grep -Psiq -- '^\h*umask\h+(0?[0-7][2-7]7|u(=[rwx]{0,3}),g=([rx]{0,2}),o=)(\h*#.*)?$' "$l_file"; then
+ l_out="$l_out\n - umask is set correctly in \"$l_file\""
+ elif grep -Psiq -- '^\h*umask\h+(([0-7][0-7][01][0-7]\b|[0-7][0-7][0-7][0-6]\b)|([0-7][01][0-7]\b|[0-7][0-7][0-6]\b)|(u=[rwx]{1,3},)?(((g=[rx]?[rx]?w[rx]?[rx]?\b)(,o=[rwx]{1,3})?)|((g=[wrx]{1,3},)?o=[wrx]{1,3}\b)))' "$l_file"; then
+ l_output2="$l_output2\n - \"$l_file\""
+ fi
+ }
+ while IFS= read -r -d $'\0' l_file; do
+ file_umask_chk
+ done < <(find /etc/profile.d/ -type f -name '*.sh' -print0)
+ [ -n "$l_out" ] && l_output="$l_out"
+ l_file="/etc/profile" && file_umask_chk
+ l_file="/etc/bashrc" && file_umask_chk
+ l_file="/etc/bash.bashrc" && file_umask_chk
+ l_file="/etc/pam.d/postlogin"
+ if grep -Psiq '^\h*session\h+[^#\n\r]+\h+pam_umask\.so\h+([^#\n\r]+\h+)?umask=(([0-7][0-7][01][0-7]\b|[0-7][0-7][0-7][0-6]\b)|([0-7][01][0-7]\b))' "$l_file"; then
+ l_output2="$l_output2\n - \"$l_file\""
+ fi
+ l_file="/etc/login.defs" && file_umask_chk
+ l_file="/etc/default/login" && file_umask_chk
+ if [ -z "$l_output2" ]; then
+ echo -e " - No files contain a UMASK that is not restrictive enough\n No UMASK updates required to existing files"
+ else
+ echo -e "\n - UMASK is not restrictive enough in the following file(s):$l_output2\n\n- Remediation Procedure:\n - Update these files and comment out the UMASK line\n or update umask to be \"0027\" or more restrictive"
+ fi
+ if [ -n "$l_output" ]; then
+ echo -e "$l_output"
+ else
+ echo -e " - Configure UMASK in a file in the \"/etc/profile.d/\" directory ending in \".sh\"\n\n Example Command (Hash to represent being run at a root prompt):\n\n# printf '%s\\\n' \"umask 027\" > /etc/profile.d/50-systemwide_umask.sh\n"
+ fi
+} +
+ + Note: + + + This method only applies to bash and shell. If other shells are supported on the system, it is recommended that their configuration files also are checked + +If the pam_umask.so + module is going to be used to set umask +, ensure that it's not being overridden by another setting. Refer to the PAM_UMASK(8) man page for more information + +
+
+
+ + + + + + +
+
+
+
+ + Logging and Auditing + + The items in this section describe how to configure logging, log monitoring, and auditing, using tools included in most distributions. + +It is recommended that rsyslog +be used for logging (with logwatch +providing summarization) and auditd + be used for auditing (with aureport +providing summarization) to automatically monitor logs for intrusion attempts and other suspicious system behavior. + In addition to the local log files created by the steps in this section, it is also recommended that sites collect copies of their system logs on a secure, centralized log server via an encrypted connection. Not only does centralized logging help sites correlate events that may be occurring on multiple systems, but having a second copy of the system log information may be critical after a system compromise where the attacker has modified the local log files on the affected system(s). If a log correlation system is deployed, configure it to process the logs described in this section. + +Because it is often necessary to correlate log information from many different systems (particularly after a security incident) it is recommended that the time be synchronized among systems and devices connected to the local network. The standard Internet protocol for time synchronization is the Network Time Protocol (NTP), which is supported by most network-ready devices. Reference < http://chrony.tuxfamily.org/ +> manual page for more information on configuring chrony. + It is important that all logs described in this section be monitored on a regular basis and correlated to determine trends. A seemingly innocuous entry in one log could be more significant when compared to an entry in another log. + + Note on log file permissions: + There really isn't a "one size fits all" solution to the permissions on log files. Many sites utilize group permissions so that administrators who are in a defined security group, such as "wheel" do not have to elevate privileges to root in order to read log files. Also, if a third party log aggregation tool is used, it may need to have group permissions to read the log files, which is preferable to having it run setuid to root. Therefore, there are two remediation and audit steps for log file permissions. One is for systems that do not have a secured group method implemented that only permits root to read the log files ( root:root 600 +). The other is for sites that do have such a setup and are designated as root:securegrp 640 +where securegrp + is the defined security group (in some cases wheel +). + + + Configure Logging + + Logging services should be configured to prevent information leaks and to aggregate logs on a remote server so that they can be reviewed in the event of a system compromise. A centralized log server provides a single point of entry for further analysis, monitoring and filtering. + Security principals for logging + + Ensure transport layer security is implemented between the client and the log server. + Ensure that logs are rotated as per the environment requirements. + Ensure all locally generated logs have the appropriate permissions. + Ensure all security logs are sent to a remote log server. + Ensure the required events are logged. + + What is covered + +This section will cover the minimum best practices for the usage of either + rsyslog + or + journald +. The recommendations are written such that each is wholly independent of each other and only one is implemented +. + + +If your organization makes use of an enterprise wide logging system completely outside of rsyslog + or journald +, then the following recommendations do not directly apply. However, the principals of the recommendations should be followed regardless of what solution is implemented. If the enterprise solution incorporates either of these tools, careful consideration should be given to the following recommendations to determine exactly what applies. + +Should your organization make use of both rsyslog + and journald +, take care how the recommendations may or may not apply to you. + + What is not covered + + +Enterprise logging systems not utilizing rsyslog + or journald +. +As logging is very situational and dependent on the local environment, not everything can be covered here. + +Transport layer security should be applied to all remote logging functionality. Both rsyslog + and journald + supports secure transport and should be configured as such. + The log server. There are a multitude of reasons for a centralized log server (and keeping a short period of logging on the local system), but the log server is out of scope for these recommendations. + + + + Configure rsyslog + + +The rsyslog + software package may be used instead of the default journald + logging mechanism. + + Note: + This section only applies if rsyslog + is the chosen method for client side logging. Do not apply this section if journald + is used. + + + Ensure rsyslog is installed + + +The rsyslog + software is recommended in environments where journald + does not meet operation requirements. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + +The security enhancements of rsyslog + such as connection-oriented (i.e. TCP) transmission of logs, the option to log to database formats, and the encryption of log data en route to a central logging server) justify installing and configuring the package. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + +Run the following command to install rsyslog +: + # yum install rsyslog + + + + + + + + + + + + Ensure rsyslog service is enabled + + +Once the rsyslog + package is installed, ensure that the service is enabled. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + +If the rsyslog + service is not enabled to start on boot, the system will not capture logging events. + + + + + NIST SP 800-53 Rev. 5: AU-3, AU-12 + + + + +Run the following command to enable rsyslog +: + # systemctl --now enable rsyslog + + + + + + + + + + + + + Ensure journald is configured to send logs to rsyslog + + +Data from systemd-journald + may be stored in volatile memory or persisted locally on the server. Utilities exist to accept remote export of systemd-journald + logs, however, use of the rsyslog + service provides a consistent means of log collection and export. + + + + + + + Network + Detect + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + Network + Detect + + + + + +As noted in the systemd-journald + man pages, systemd-journald + logs may be exported to rsyslog + either through the process mentioned here, or through a facility like systemd-journald.service +. There are trade-offs involved in each implementation, where ForwardToSyslog + will immediately capture all events (and forward to an external log server, if properly configured), but may not capture all boot-up activities. Mechanisms such as systemd-journald.service +, on the other hand, will record bootup events, but may delay sending the information to rsyslog +, leading to the potential for log manipulation prior to export. Be aware of the limitations of all tools employed to secure a system. + + + + + -IF- + rsyslog + is the preferred method for capturing logs, all logs of the system should be sent to it for further processing. + + Note: + This recommendation only applies if rsyslog + is the chosen method for client side logging. Do not apply this recommendation if systemd-journald + is used. + + + + + + + NIST SP 800-53 Rev. 5: AC-3, AU-2, AU-4, AU-12, MP-2, SI-5 + SYSTEMD-JOURNALD.SERVICE(8) + JOURNALD.CONF(5) + + + + +Create or edit the file /etc/systemd/journald.conf + and add or edit the following line: + ForwardToSyslog=yes + + +Reload the systemd-journald + service: + # systemctl systemctl reload-or-try-restart systemd-journald.service + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure rsyslog default file permissions are configured + + RSyslog will create logfiles that do not already exist on the system. This setting controls what permissions will be applied to these newly created files. + + + + + + + Data + Protect + + + + Network + Detect + + + + + + Applications + Protect + + + + Network + Detect + + + + Network + Detect + + + + + + It is important to ensure that log files have the correct permissions to ensure that sensitive data is archived and protected. + + + + + + + See the rsyslog.conf(5) man page for more information. + + + + +Edit either /etc/rsyslog.conf + or a dedicated .conf + file in /etc/rsyslog.d/ + and set $FileCreateMode + to 0640 + or more restrictive: + $FileCreateMode 0640 + + Restart the service: + # systemctl restart rsyslog + + Impact: + + +The systems global umask + could override, but only making the file permissions stricter, what is configured in RSyslog with the FileCreateMode + directive. RSyslog also has its own $umask + directive that can alter the intended file creation mode. In addition, consideration should be given to how FileCreateMode + is used. + +Thus it is critical to ensure that the intended file creation mode is not overridden with less restrictive settings in /etc/rsyslog.conf +, /etc/rsyslog.d/*conf + files and that FileCreateMode + is set before any file is created. + + + + + + + + + + + + + + + Ensure logging is configured + + +The /etc/rsyslog.conf + and /etc/rsyslog.d/*.conf + files specifies rules for logging and which files are to be used to log certain classes of messages. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + +A great deal of important security-related information is sent via rsyslog + (e.g., successful and failed su attempts, failed login attempts, root login attempts, etc.). + + + + + See the rsyslog.conf(5) man page for more information. + + + + +Edit the following lines in the /etc/rsyslog.conf + and /etc/rsyslog.d/*.conf + files as appropriate for your environment. + + Note: + The below configuration is shown for example purposes only. Due care should be given to how the organization wish to store log data. + +*.emerg :omusrmsg:*
+auth,authpriv.* /var/log/secure
+mail.* -/var/log/mail
+mail.info -/var/log/mail.info
+mail.warning -/var/log/mail.warn
+mail.err /var/log/mail.err
+cron.* /var/log/cron
+*.=warning;*.=err -/var/log/warn
+*.crit /var/log/warn
+*.*;mail.none;news.none -/var/log/messages
+local0,local1.* -/var/log/localmessages
+local2,local3.* -/var/log/localmessages
+local4,local5.* -/var/log/localmessages
+local6,local7.* -/var/log/localmessages +
+ +Run the following command to reload the rsyslogd + configuration: + # systemctl restart rsyslog + +
+
+
+
+ + Ensure rsyslog is configured to send logs to a remote log host + + RSyslog supports the ability to send log events it gathers to a remote log host or to receive messages from remote hosts, thus enabling centralized log management. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + +In addition, see the RSyslog documentation + for implementation details of TLS. + + + + Storing log data on a remote host protects log integrity from local attacks. If an attacker gains root access on the local system, they could tamper with or remove log data that is stored on the local system. + + + + + See the rsyslog.conf(5) man page for more information. + + + + +Edit the /etc/rsyslog.conf + and /etc/rsyslog.d/*.conf + files and add the following line (where loghost.example.com + is the name of your central log host). The target + directive may either be a fully qualified domain name or an IP address. + +*.* action(type="omfwd" target="192.168.2.100" port="514" protocol="tcp"
+ action.resumeRetryCount="100"
+ queue.type="LinkedList" queue.size="1000") +
+ +Run the following command to reload the rsyslogd + configuration: + # systemctl restart rsyslog + +
+
+
+ + + + + + + + +
+ + Ensure rsyslog is not configured to receive logs from a remote client + + RSyslog supports the ability to receive messages from remote hosts, thus acting as a log server. Clients should not receive data from other hosts. + + + + + + + Devices + Protect + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + Devices + Protect + + + + + + If a client is configured to also receive data, thus turning it into a server, the client system is acting outside its operational boundary. + + + + + + + + + + +Should there be any active log server configuration found in the auditing section, modify those files and remove the specific lines highlighted by the audit. Ensure none of the following entries are present in any of /etc/rsyslog.conf + or /etc/rsyslog.d/*.conf +. + + New format + + +module(load="imtcp")
+input(type="imtcp" port="514") +
+ +- OR +- + + Old format + + +$ModLoad imtcp
+$InputTCPServerRun +
+ Restart the service: + # systemctl restart rsyslog + +
+
+
+ + + + + + + + +
+
+ + Configure journald + + +Included in the systemd suite is a journaling service called systemd-journald.service + for the collection and storage of logging data. It creates and maintains structured, indexed journals based on logging information that is received from a variety of sources such as: + + Classic RFC3164 BSD syslog via the /dev/log socket + STDOUT/STDERR of programs via StandardOutput=journal + StandardError=journal in service files (both of which are default settings) + Kernel log messages via the /dev/kmsg device node + Audit records via the kernel’s audit subsystem + Structured log messages via journald’s native protocol + + +Any changes made to the systemd-journald + configuration will require a re-start of systemd-journald + + + + Ensure journald is configured to send logs to a remote log host + + + Ensure systemd-journal-remote is installed + + +Journald (via systemd-journal-remote +) supports the ability to send log events it gathers to a remote log host or to receive messages from remote hosts, thus enabling centralized log management. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + Storing log data on a remote host protects log integrity from local attacks. If an attacker gains root access on the local system, they could tamper with or remove log data that is stored on the local system. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + +Run the following command to install system-journal-gateway +: + # yum install system-journal-gateway + + + + + + + + + + + + + + + Ensure systemd-journal-remote is configured + + +Journald (via systemd-journal-remote +) supports the ability to send log events it gathers to a remote log host or to receive messages from remote hosts, thus enabling centralized log management. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + Storing log data on a remote host protects log integrity from local attacks. If an attacker gains root access on the local system, they could tamper with or remove log data that is stored on the local system. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + +Edit the /etc/systemd/journal-upload.conf + file and ensure the following lines are set per your environment: + +URL=192.168.50.42
+ServerKeyFile=/etc/ssl/private/journal-upload.pem
+ServerCertificateFile=/etc/ssl/certs/journal-upload.pem
+TrustedCertificateFile=/etc/ssl/ca/trusted.pem +
+ Restart the service: + # systemctl restart systemd-journal-upload + +
+
+
+
+ + Ensure systemd-journal-remote is enabled + + +Journald (via systemd-journal-remote +) supports the ability to send log events it gathers to a remote log host or to receive messages from remote hosts, thus enabling centralized log management. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + Storing log data on a remote host protects log integrity from local attacks. If an attacker gains root access on the local system, they could tamper with or remove log data that is stored on the local system. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, CM-7, SI-5 + + + + +Run the following command to enable systemd-journal-remote +: + # systemctl --now enable systemd-journal-upload.service + + + + + + + + + + + + + Ensure journald is not configured to receive logs from a remote client + + Journald supports the ability to receive messages from remote hosts, thus acting as a log server. Clients should not receive data from other hosts. + + NOTE: + + + +The same package, systemd-journal-remote +, is used for both sending logs to remote hosts and receiving incoming logs. + +With regards to receiving logs, there are two services; systemd-journal-remote.socket + and systemd-journal-remote.service +. + + + + + + + + Devices + Protect + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + Devices + Protect + + + + + + If a client is configured to also receive data, thus turning it into a server, the client system is acting outside it's operational boundary. + + + + + + + + + + +Run the following command to disable systemd-journal-remote.socket +: + # systemctl --now mask systemd-journal-remote.socket + + + + + + + + + + + +
+ + Ensure journald service is enabled + + +Ensure that the systemd-journald + service is enabled to allow capturing of logging events. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + +If the systemd-journald + service is not enabled to start on boot, the system will not capture logging events. + + + + + + + + +By default the systemd-journald + service does not have an [Install] + section and thus cannot be enabled / disabled. It is meant to be referenced as Requires + or Wants + by other unit files. As such, if the status of systemd-journald + is not static +, investigate why. + + + + + + + + + + + + Ensure journald is configured to compress large log files + + The journald system includes the capability of compressing overly large files to avoid filling up the system with logs or making the logs unmanageably large. + + + + + + + Network + Detect + + + + Network + Protect + + + + + + Network + Detect + + + + Network + Detect + + + + Network + Detect + + + + + +The main configuration file /etc/systemd/journald.conf + is read before any of the custom *.conf files. If there are custom configs present, they override the main configuration parameters. + It is possible to change the default threshold of 512 bytes per object before compression is used. + + + + Uncompressed large files may unexpectedly fill a filesystem leading to resource unavailability. Compressing logs prior to write can prevent sudden, unexpected filesystem impacts. + + + + + + + + + + +Edit the /etc/systemd/journald.conf + file and add the following line: + Compress=yes + + Restart the service: + # systemctl restart systemd-journald.service + + + + + + + + + + + + Ensure journald is configured to write logfiles to persistent disk + + Data from journald may be stored in volatile memory or persisted locally on the server. Logs in memory will be lost upon a system reboot. By persisting logs to local disk on the server they are protected from loss due to a reboot. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + +The main configuration file /etc/systemd/journald.conf + is read before any of the custom *.conf files. If there are custom configs present, they override the main configuration parameters. + + + + Writing log data to disk will provide the ability to forensically reconstruct events which may have impacted the operations or security of a system even after a system crash or reboot. + + + + + + + + +Edit the /etc/systemd/journald.conf + file and add the following line: + Storage=persistent + + Restart the service: + # systemctl restart systemd-journald.service + + + + + + + + + + + + Ensure journald is not configured to send logs to rsyslog + + +Data from journald + should be kept in the confines of the service and not forwarded on to other services. + + + + + + + Network + Detect + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + Network + Detect + + + + + + + IF + journald is the method for capturing logs, all logs of the system should be handled by journald and not forwarded to other logging mechanisms. + + Note: + This recommendation only applies if journald is the chosen method for client side logging. Do not apply this recommendation if rsyslog is used. + + + + + + + + + + +Edit the /etc/systemd/journald.conf + file and ensure that ForwardToSyslog=yes + is removed. + Restart the service: + # systemctl systemctl reload-or-try-restart systemd-journald.service + + + + + + + + + + + + Ensure journald log rotation is configured per site policy + + +Journald includes the capability of rotating log files regularly to avoid filling up the system with logs or making the logs unmanageably large. The file /etc/systemd/journald.conf + is the configuration file used to specify how logs generated by Journald should be rotated. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + +See man 5 journald.conf + for detailed information regarding the parameters in use. + + + + By keeping the log files smaller and more manageable, a system administrator can easily archive these files to another system and spend less time looking through inordinately large log files. + + + + + + + + +Review /etc/systemd/journald.conf + and verify logs are rotated according to site policy. The settings should be carefully understood as there are specific edge cases and prioritization of parameters. + The specific parameters for log rotation are: + +SystemMaxUse=
+SystemKeepFree=
+RuntimeMaxUse=
+RuntimeKeepFree=
+MaxFileSec= +
+
+
+
+
+
+ + Ensure logrotate is configured + + +The system includes the capability of rotating log files regularly to avoid filling up the system with logs or making the logs unmanageably large. The file /etc/logrotate.d/syslog + is the configuration file used to rotate log files created by syslog + or rsyslog +. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +If no maxage + setting is set for logrotate + a situation can occur where logrotate + is interrupted and fails to delete rotated log files. It is recommended to set this to a value greater than the longest any log file should exist on your system to ensure that any such log file is removed but standard rotation settings are not overridden. + + + + By keeping the log files smaller and more manageable, a system administrator can easily archive these files to another system and spend less time looking through inordinately large log files. + + + + NIST SP 800-53 Rev. 5: AU-8 + + + + +Edit /etc/logrotate.conf + and /etc/logrotate.d/* + to ensure logs are rotated according to site policy. + + + + + + Ensure all logfiles have appropriate access configured + + +Log files stored in /var/log/ + contain logged information from many services on the system and potentially from other logged hosts as well. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + It is important that log files have the correct permissions to ensure that sensitive data is protected and that only the appropriate users / groups have access to them. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following script to update permissions and ownership on files in /var/log +. + Although the script is not destructive, ensure that the output is captured in the event that the remediation causes issues. + +#!/usr/bin/env bash
+
+{
+ l_op2="" l_output2=""
+ l_uidmin="$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)"
+ file_test_fix()
+ {
+ l_op2=""
+ l_fuser="root"
+ l_fgroup="root"
+ if [ $(( $l_mode & $perm_mask )) -gt 0 ]; then
+ l_op2="$l_op2\n - Mode: \"$l_mode\" should be \"$maxperm\" or more restrictive\n - Removing excess permissions"
+ chmod "$l_rperms" "$l_fname"
+ fi
+ if [[ ! "$l_user" =~ $l_auser ]]; then
+ l_op2="$l_op2\n - Owned by: \"$l_user\" and should be owned by \"${l_auser//|/ or }\"\n - Changing ownership to: \"$l_fuser\""
+ chown "$l_fuser" "$l_fname"
+ fi
+ if [[ ! "$l_group" =~ $l_agroup ]]; then
+ l_op2="$l_op2\n - Group owned by: \"$l_group\" and should be group owned by \"${l_agroup//|/ or }\"\n - Changing group ownership to: \"$l_fgroup\""
+ chgrp "$l_fgroup" "$l_fname"
+ fi
+ [ -n "$l_op2" ] && l_output2="$l_output2\n - File: \"$l_fname\" is:$l_op2\n"
+ }
+ unset a_file && a_file=() # clear and initialize array
+ # Loop to create array with stat of files that could possibly fail one of the audits
+ while IFS= read -r -d $'\0' l_file; do
+ [ -e "$l_file" ] && a_file+=("$(stat -Lc '%n^%#a^%U^%u^%G^%g' "$l_file")")
+ done < <(find -L /var/log -type f \( -perm /0137 -o ! -user root -o ! -group root \) -print0)
+ while IFS="^" read -r l_fname l_mode l_user l_uid l_group l_gid; do
+ l_bname="$(basename "$l_fname")"
+ case "$l_bname" in
+ lastlog | lastlog.* | wtmp | wtmp.* | wtmp-* | btmp | btmp.* | btmp-* | README)
+ perm_mask='0113'
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_rperms="ug-x,o-wx"
+ l_auser="root"
+ l_agroup="(root|utmp)"
+ file_test_fix
+ ;;
+ secure | auth.log | syslog | messages)
+ perm_mask='0137'
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_rperms="u-x,g-wx,o-rwx"
+ l_auser="(root|syslog)"
+ l_agroup="(root|adm)"
+ file_test_fix
+ ;;
+ SSSD | sssd)
+ perm_mask='0117'
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_rperms="ug-x,o-rwx"
+ l_auser="(root|SSSD)"
+ l_agroup="(root|SSSD)"
+ file_test_fix
+ ;;
+ gdm | gdm3)
+ perm_mask='0117'
+ l_rperms="ug-x,o-rwx"
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_auser="root"
+ l_agroup="(root|gdm|gdm3)"
+ file_test_fix
+ ;;
+ *.journal | *.journal~)
+ perm_mask='0137'
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_rperms="u-x,g-wx,o-rwx"
+ l_auser="root"
+ l_agroup="(root|systemd-journal)"
+ file_test_fix
+ ;;
+ *)
+ perm_mask='0137'
+ maxperm="$( printf '%o' $(( 0777 & ~$perm_mask)) )"
+ l_rperms="u-x,g-wx,o-rwx"
+ l_auser="(root|syslog)"
+ l_agroup="(root|adm)"
+ if [ "$l_uid" -lt "$l_uidmin" ] && [ -z "$(awk -v grp="$l_group" -F: '$1==grp {print $4}' /etc/group)" ]; then
+ if [[ ! "$l_user" =~ $l_auser ]]; then
+ l_auser="(root|syslog|$l_user)"
+ fi
+ if [[ ! "$l_group" =~ $l_agroup ]]; then
+ l_tst=""
+ while l_out3="" read -r l_duid; do
+ [ "$l_duid" -ge "$l_uidmin" ] && l_tst=failed
+ done <<< "$(awk -F: '$4=='"$l_gid"' {print $3}' /etc/passwd)"
+ [ "$l_tst" != "failed" ] && l_agroup="(root|adm|$l_group)"
+ fi
+ fi
+ file_test_fix
+ ;;
+ esac
+ done <<< "$(printf '%s\n' "${a_file[@]}")"
+ unset a_file # Clear array
+ # If all files passed, then we report no changes
+ if [ -z "$l_output2" ]; then
+ echo -e "- All files in \"/var/log/\" have appropriate permissions and ownership\n - No changes required\n"
+ else
+ # print report of changes
+ echo -e "\n$l_output2"
+ fi
+} +
+ + Note: + You may also need to change the configuration for your logging software or services for any logs that had incorrect permissions. + If there are services that log to other locations, ensure that those log files have the appropriate access configured. +
+
+
+ + + + + + +
+
+ + Configure System Accounting (auditd) + + +The Linux Auditing System operates on a set of rules that collects certain types of system activity to facilitate incident investigation, detect unauthorized access or modification of data. By default events will be logged to /var/log/audit/audit.log +, which can be configured in /etc/audit/auditd.conf +. + The following types of audit rules can be specified: + + Control rules: Configuration of the auditing system. + File system rules: Allow the auditing of access to a particular file or a directory. Also known as file watches. + System call rules: Allow logging of system calls that any specified program makes. + + Audit rules can be set: + + +On the command line using the auditctl + utility. These rules are not persistent across reboots. + +In /etc/audit/audit.rules +. These rules have to be merged and loaded before they are active. + + + Notes: + + + +For 64 bit systems that have arch + as a rule parameter, you will need two rules: one for 64 bit and one for 32 bit systems calls. For 32 bit systems, only one rule is needed. + +If the auditing system is configured to be locked ( -e 2 +), a system reboot will be required in order to load any changes. + Key names are optional on the rules and will not be used in compliance auditing. The usage of key names is highly recommended as it facilitates organization and searching; as such, all remediation steps will have key names supplied. + +It is best practice to store the rules, in number prepended files, in /etc/audit/rules.d/ +. Rules must end in a .rules + suffix. This then requires the use of augenrules + to merge all the rules into /etc/audit/audit.rules + based on their alphabetical (lexical) sort order. All benchmark recommendations follow this best practice for remediation, specifically using the prefix of 50 + which is center weighed if all rule sets make use of the number prepending naming convention. + +Your system may have been customized to change the default UID_MIN +. All sample output uses 1000 +, but this value will not be used in compliance auditing. To confirm the UID_MIN + for your system, run the following command: awk '/^\s*UID_MIN/{print $2}' /etc/login.defs + + + Normalization + The Audit system normalizes some entries, so when you look at the sample output keep in mind that: + + +With regards to users whose login UID is not set, the values -1 + / unset + / 4294967295 + are equivalent and normalized to -1 +. + +When comparing field types and both sides of the comparison is valid fields types, such as euid!=uid +, then the auditing system may normalize such that the output is uid!=euid +. + Some parts of the rule may be rearranged whilst others are dependent on previous syntax. For example, the following two statements are the same: + + -a always,exit -F arch=b64 -S execve -C uid!=euid -F auid!=-1 -F key=user_emulation + + and + -a always,exit -F arch=b64 -C euid!=uid -F auid!=unset -S execve -k user_emulation + + Capacity planning + The recommendations in this section implement auditing policies that not only produce large quantities of logged data, but may also negatively impact system performance. Capacity planning is critical in order not to adversely impact production environments. + + Disk space. If a significantly large set of events are captured, additional on system or off system storage may need to be allocated. If the logs are not sent to a remote log server, ensure that log rotation is implemented else the disk will fill up and the system will halt. Even when logs are sent to a log server, ensure sufficient disk space to allow caching of logs in the case of temporary network outages. + Disk IO. It is not just the amount of data collected that should be considered, but the rate at which logs are generated. + CPU overhead. System call rules might incur considerable CPU overhead. Test the systems open/close syscalls per second with and without the rules to gauge the impact of the rules. + + + + Ensure auditing is enabled + + The capturing of system events provides system administrators with information to allow them to determine if unauthorized access to their system is occurring. + + + Ensure audit is installed + + + auditd + is the userspace component to the Linux Auditing System. It's responsible for writing audit records to the disk. + + + + + + + Network + Detect + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + The capturing of system events provides system administrators with information to allow them to determine if unauthorized access to their system is occurring. + + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-3, AU-3(1), AU-12, SI-5 + + + + +Run the following command to install audit + and audit-libs +: + # yum install audit audit-libs + + + + + + + + + + + + + + + Ensure auditing for processes that start prior to auditd is enabled + + +Configure grub2 + so that processes that are capable of being audited can be audited even if they start up prior to auditd + startup. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + This recommendation is designed around the grub 2 bootloader, if another bootloader is in use in your environment enact equivalent settings. + + grubby + is a command line tool used to configure bootloader menu entries across multiple architectures. It is used for updating and displaying information about the configuration files for various architecture specific bootloaders. + It is primarily designed to be used from scripts which install new kernels and need to find information about the current boot environment. + The grubby executable has full support for the grub2 bootloader on x86_64 systems using legacy BIOS or modern UEFI firmware and ppc64 and ppc64le hardware using OPAL or SLOF as firmware. + Legacy s390 and the current s390x architectures and their zipl bootloader are fully supported. + Support for yaboot has been deprecated as all ppc architecture hardware since the Power8 uses grub2 or petitboot which both use the grub2 configuration file format. + Legacy bootloaders LILO, SILO, and ELILO are deprecated and no longer receiving active support in favor of previously mentioned bootloaders. + The default bootloader target is primarily determined by the architecture for which grubby has been built. Each architecture has a preferred bootloader, and each bootloader has its own configuration file. If no bootloader is selected on the command line, grubby will use these default settings to search for an existing configuration. If no bootloader configuration file is found, grubby will use the default value for that architecture. + + + + +Audit events need to be captured on processes that start up prior to auditd + , so that potential malicious activity cannot go undetected. + + + + + + + +Run the following command to update the grub2 + configuration with audit=1 +: + # grubby --update-kernel ALL --args 'audit=1' + + +Edit /etc/default/grub + and add audit=1 + to the GRUB_CMDLINE_LINUX= + line between the opening and closing double quotes: + + Example: + + GRUB_CMDLINE_LINUX="quiet audit=1" + + + Note: + Other parameters may also be listed + + + + + + + + + + + + + + + + Ensure audit_backlog_limit is sufficient + + +The audit_backlog_limit + parameter determines how auditd records can be held in the auditd backlog. The default setting of 64 may be insufficient to store all audit events during boot. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + This recommendation is designed around the grub 2 bootloader, if another bootloader is in use in your environment enact equivalent settings. + + grubby + is a command line tool used to configure bootloader menu entries across multiple architectures. It is used for updating and displaying information about the configuration files for various architecture specific bootloaders. + It is primarily designed to be used from scripts which install new kernels and need to find information about the current boot environment. + The grubby executable has full support for the grub2 bootloader on x86_64 systems using legacy BIOS or modern UEFI firmware and ppc64 and ppc64le hardware using OPAL or SLOF as firmware. + Legacy s390 and the current s390x architectures and their zipl bootloader are fully supported. + Support for yaboot has been deprecated as all ppc architecture hardware since the Power8 uses grub2 or petitboot which both use the grub2 configuration file format. + Legacy bootloaders LILO, SILO, and ELILO are deprecated and no longer receiving active support in favor of previously mentioned bootloaders. + The default bootloader target is primarily determined by the architecture for which grubby has been built. Each architecture has a preferred bootloader, and each bootloader has its own configuration file. If no bootloader is selected on the command line, grubby will use these default settings to search for an existing configuration. If no bootloader configuration file is found, grubby will use the default value for that architecture. + + + + +During boot if audit=1 +, then the backlog will hold 64 records. If more than 64 records are created during boot, auditd records will be lost and potential malicious activity could go undetected. + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + +Run the following command to add audit_backlog_limit=<BACKLOG SIZE> + to GRUB_CMDLINE_LINUX: + # grubby --update-kernel ALL --args 'audit_backlog_limit=<BACKLOG SIZE>' + + + Example: + + # grubby --update-kernel ALL --args 'audit_backlog_limit=8192' + + +Edit /etc/default/grub + and add audit_backlog_limit=<BACKLOG SIZE> + to the GRUB_CMDLINE_LINUX= + line between the opening and closing double quotes: + + Example: + + GRUB_CMDLINE_LINUX="quiet audit_backlog_limit=8192" + + + Note: + Other parameters may also be listed + + + + + + + + + + + + + + + + Ensure auditd service is enabled + + +Turn on the auditd + daemon to record system events. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + Additional methods of enabling a service exist. Consult your distribution documentation for appropriate methods. + + + + The capturing of system events provides system administrators with information to allow them to determine if unauthorized access to their system is occurring. + + + + + NIST SP 800-53 Rev. 5: AU-2, AU-12, SI-5 + + + + +Run the following command to enable auditd +: + # systemctl --now enable auditd + + + + + + + + + + + + + + Configure Data Retention + + When auditing, it is important to carefully configure the storage requirements for audit logs. By default, auditd will max out the log files at 5MB and retain only 4 copies of them. Older versions will be deleted. It is possible on a system that the 20 MBs of audit logs may fill up the system causing loss of audit data. While the recommendations here provide guidance, check your site policy for audit storage requirements. + + + Ensure audit log storage size is configured + + Configure the maximum size of the audit log file. Once the log reaches the maximum size, it will be rotated and a new log file will be started. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + +The max_log_file + parameter is measured in megabytes. + +Other methods of log rotation may be appropriate based on site policy. One example is time-based rotation strategies which don't have native support in auditd + configurations. Manual audit of custom configurations should be evaluated for effectiveness and completeness. + + + + It is important that an appropriate size is determined for log files so that they do not impact the system and audit data is not lost. + + + + NIST SP 800-53 Rev. 5: AU-8 + + + + +Set the following parameter in /etc/audit/auditd.conf + in accordance with site policy: + max_log_file = <MB> + + + + + + + + + + + + Ensure audit logs are not automatically deleted + + +The max_log_file_action + setting determines how to handle the audit log file reaching the max file size. A value of keep_logs + will rotate the logs but never delete old logs. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + + In high security contexts, the benefits of maintaining a long audit history exceed the cost of storing the audit history. + + + + NIST SP 800-53 Rev. 5: AU-8 + + + + +Set the following parameter in /etc/audit/auditd.conf: + + max_log_file_action = keep_logs + + + + + + + + + + + + Ensure system is disabled when audit logs are full + + +The auditd + daemon can be configured to halt the system or put the system in single user mode, if no free space is available or an error is detected on the partition that holds the audit log files. + +The disk_full_action + parameter tells the system what action to take when no free space is available on the partition that holds the audit log files. Valid values are ignore +, syslog +, rotate +, exec +, suspend +, single +, and halt +. + + + ignore +, the audit daemon will issue a syslog message but no other action is taken + + syslog +, the audit daemon will issue a warning to syslog + + rotate +, the audit daemon will rotate logs, losing the oldest to free up space + + exec +, /path-to-script will execute the script. You cannot pass parameters to the script. The script is also responsible for telling the auditd daemon to resume logging once its completed its action + + suspend +, the audit daemon will stop writing records to the disk + + single +, the audit daemon will put the computer system in single user mode + + halt +, the audit daemon will shut down the system + + +The disk_error_action + parameter tells the system what action to take when an error is detected on the partition that holds the audit log files. Valid values are ignore +, syslog +, exec +, suspend +, single +, and halt +. + + + ignore +, the audit daemon will not take any action + + syslog +, the audit daemon will issue no more than 5 consecutive warnings to syslog + + exec +, /path-to-script will execute the script. You cannot pass parameters to the script + + suspend +, the audit daemon will stop writing records to the disk + + single +, the audit daemon will put the computer system in single user mode + + halt +, the audit daemon will shut down the system + + + + + + + + Network + Detect + + + + Network + Protect + + + + + + In high security contexts, the risk of detecting unauthorized access or nonrepudiation exceeds the benefit of the system's availability. + + + + NIST SP 800-53 Rev. 5: AU-2, AU-8, AU-12, SI-5 + AUDITD.CONF(5) + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/auditing-the-system_security-hardening#configuring-auditd-for-a-secure-environment_auditing-the-system + + + + +Set one of the following parameters in /etc/audit/auditd.conf + depending on your local security policies. + +disk_full_action = <halt|single>
+disk_error_action = <syslog|single|halt> +
+ + Example: + + +disk_full_action = halt
+disk_error_action = halt +
+ Impact: + + + -IF- + + + +The disk_full_action + parameter is set to halt + the auditd + daemon will shutdown the system when the disk partition containing the audit logs becomes full. + +The disk_full_action + parameter is set to single + the auditd + daemon will put the computer system in single user mode when the disk partition containing the audit logs becomes full. + + + -IF- + + + +The disk_error_action + parameter is set to halt + the auditd + daemon will shutdown the system when an error is detected on the partition that holds the audit log files. + +The disk_error_action + parameter is set to single + the auditd + daemon will put the computer system in single user mode when an error is detected on the partition that holds the audit log files. + +The disk_error_action + parameter is set to syslog + the auditd + daemon will issue no more than 5 consecutive warnings to syslog when an error is detected on the partition that holds the audit log files. + + +
+
+
+ + + + + + + + +
+ + Ensure system warns when audit logs are low on space + + +The auditd + daemon can be configured to halt the system, put the system in single user mode or send a warning message, if the partition that holds the audit log files is low on space. + +The space_left_action + parameter tells the system what action to take when the system has detected that it is starting to get low on disk space. Valid values are ignore +, syslog +, rotate +, email +, exec +, suspend +, single +, and halt +. + + + ignore +, the audit daemon does nothing + + syslog +, the audit daemon will issue a warning to syslog + + rotate +, the audit daemon will rotate logs, losing the oldest to free up space + + email +, the audit daemon will send a warning to the email account specified in action_mail_acct + as well as sending the message to syslog + + exec +, /path-to-script will execute the script. You cannot pass parameters to the script. The script is also responsible for telling the auditd daemon to resume logging once its completed its action + + suspend +, the audit daemon will stop writing records to the disk + + single +, the audit daemon will put the computer system in single user mode + + halt +, the audit daemon will shut down the system + + +The admin_space_left_action + parameter tells the system what action to take when the system has detected that it is low on disk space. Valid values are ignore +, syslog +, rotate +, email +, exec +, suspend +, single +, and halt +. + + + ignore +, the audit daemon does nothing + + syslog +, the audit daemon will issue a warning to syslog + + rotate +, the audit daemon will rotate logs, losing the oldest to free up space + + email +, the audit daemon will send a warning to the email account specified in action_mail_acct + as well as sending the message to syslog + + exec +, /path-to-script will execute the script. You cannot pass parameters to the script. The script is also responsible for telling the auditd daemon to resume logging once its completed its action + + suspend +, the audit daemon will stop writing records to the disk + + single +, the audit daemon will put the computer system in single user mode + + halt +, the audit daemon will shut down the system + + + + + + + + Network + Detect + + + + Network + Protect + + + + + + In high security contexts, the risk of detecting unauthorized access or nonrepudiation exceeds the benefit of the system's availability. + + + + NIST SP 800-53 Rev. 5: AU-2, AU-8, AU-12, SI-5 + AUDITD.CONF(5) + https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/security_hardening/auditing-the-system_security-hardening#configuring-auditd-for-a-secure-environment_auditing-the-system + + + + +Set the space_left_action + parameter in /etc/audit/auditd.conf + to email +, exec +, single +, or halt +: + + Example: + + space_left_action = email + + +Set the admin_space_left_action + parameter in /etc/audit/auditd.conf + to single + or halt +: + + Example: + + admin_space_left_action = single + + + Note: + A Mail Transfer Agent (MTA) must be installed and configured properly to set space_left_action = email + + Impact: + + +If the admin_space_left_action + is set to single + the audit daemon will put the computer system in single user mode. + + + + + + + + + + + + + +
+ + Configure auditd rules + + The Audit system operates on a set of rules that define what is to be captured in the log files. + The following types of Audit rules can be specified: + + Control rules: Allow the Audit system's behavior and some of its configuration to be modified. + File system rules: Allow the auditing of access to a particular file or a directory. (Also known as file watches) + System call rules: Allow logging of system calls that any specified program makes. + + Audit rules can be set: + + on the command line using the auditctl utility. Note that these rules are not persistent across reboots. + +in a file ending in .rules + in the /etc/audit/audit.d/ + directory. + + + + Ensure changes to system administration scope (sudoers) is collected + + +Monitor scope changes for system administrators. If the system has been properly configured to force system administrators to log in as themselves first and then use the sudo + command to execute privileged commands, it is possible to monitor changes in scope. The file /etc/sudoers +, or files in /etc/sudoers.d +, will be written to when the file(s) or related attributes have changed. The audit records will be tagged with the identifier "scope". + + + + + + + Network + Detect + + + + + + Users + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +Changes in the /etc/sudoers + and /etc/sudoers.d + files can indicate that an unauthorized change has been made to the scope of system administrator activity. + + + + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor scope changes for system administrators. + Example: + +# printf "
+-w /etc/sudoers -p wa -k scope
+-w /etc/sudoers.d -p wa -k scope
+" >> /etc/audit/rules.d/50-scope.rules +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + +
+ + Ensure actions as another user are always logged + + + sudo + provides users with temporary elevated privileges to perform operations, either as the superuser or another user. + + + + + + + Network + Detect + + + + + + Users + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +Creating an audit log of users with temporary elevated privileges and the operation(s) they performed is essential to reporting. Administrators will want to correlate the events written to the audit trail with the records written to sudo +'s logfile to verify if unauthorized commands have been executed. + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor elevated privileges. + 64 Bit systems + Example: + +# printf "
+-a always,exit -F arch=b64 -C euid!=uid -F auid!=unset -S execve -k user_emulation
+-a always,exit -F arch=b32 -C euid!=uid -F auid!=unset -S execve -k user_emulation
+" >> /etc/audit/rules.d/50-user_emulation.rules +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. +
+
+
+ + + + + + + + + + + + +
+ + Ensure events that modify the sudo log file are collected + + +Monitor the sudo + log file. If the system has been properly configured to disable the use of the su + command and force all administrators to have to log in first and then use sudo + to execute privileged commands, then all administrator commands will be logged to /var/log/sudo.log + . Any time a command is executed, an audit event will be triggered as the /var/log/sudo.log + file will be opened for write and the executed administration command will be written to the log. + + + + + + + Network + Detect + + + + + + Users + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +Changes in /var/log/sudo.log + indicate that an administrator has executed a command or the log file itself has been tampered with. Administrators will want to correlate the events written to the audit trail with the records written to /var/log/sudo.log + to verify if unauthorized commands have been executed. + + + + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor events that modify the sudo log file. + Example: + +# {
+SUDO_LOG_FILE=$(grep -r logfile /etc/sudoers* | sed -e 's/.*logfile=//;s/,? .*//' -e 's/"//g')
+[ -n "${SUDO_LOG_FILE}" ] && printf "
+-w ${SUDO_LOG_FILE} -p wa -k sudo_log_file
+" >> /etc/audit/rules.d/50-sudo.rules || printf "ERROR: Variable 'SUDO_LOG_FILE_ESCAPED' is unset.\n"
+} +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + +
+ + Ensure events that modify date and time information are collected + + Capture events where the system date and/or time has been modified. The parameters in this section are set to determine if the; + + + adjtimex + - tune kernel clock + + settimeofday + - set time using timeval + and timezone + structures + + stime + - using seconds since 1/1/1970 + + clock_settime + - allows for the setting of several internal clocks and timers + + system calls have been executed. Further, ensure to write an audit record to the configured audit log file upon exit, tagging the records with a unique identifier such as "time-change". + + + + + + + Network + Detect + + + + + + Applications + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Unexpected changes in system date and/or time could be a sign of malicious activity on the system. + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor events that modify date and time information. + 64 Bit systems + Example: + +# printf "
+-a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time-change
+-a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime -k time-change
+-w /etc/localtime -p wa -k time-change
+" >> /etc/audit/rules.d/50-time-change.rules +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. In addition, add stime + to the system call audit. Example: + -a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime,stime -k time-change + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure events that modify the system's network environment are collected + + Record changes to network environment files or system calls. The below parameters monitors the following system calls, and write an audit event on system call exit: + + + sethostname + - set the systems host name + + setdomainname + - set the systems domain name + + The files being monitored are: + + + /etc/issue + and /etc/issue.net + - messages displayed pre-login + + /etc/hosts + - file containing host names and associated IP addresses + + /etc/sysconfig/network + - additional information that is valid to all network interfaces + + /etc/sysconfig/network-scripts/ + - directory containing network interface scripts and configurations files + + + + + + + + Network + Detect + + + + + + Applications + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +Monitoring sethostname + and setdomainname + will identify potential unauthorized changes to host and domain name of a system. The changing of these names could potentially break security parameters that are set based on those names. The /etc/hosts + file is monitored for changes that can indicate an unauthorized intruder is trying to change machine associations with IP addresses and trick users and processes into connecting to unintended machines. Monitoring /etc/issue + and /etc/issue.net + is important, as intruders could put disinformation into those files and trick users into providing information to the intruder. Monitoring /etc/sysconfig/network + is important as it can show if network interfaces or scripts are being modified in a way that can lead to the machine becoming unavailable or compromised. All audit records should have a relevant tag associated with them. + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor events that modify the system's network environment. + 64 Bit systems + Example: + +# printf "
+-a always,exit -F arch=b64 -S sethostname,setdomainname -k system-locale
+-a always,exit -F arch=b32 -S sethostname,setdomainname -k system-locale
+-w /etc/issue -p wa -k system-locale
+-w /etc/issue.net -p wa -k system-locale
+-w /etc/hosts -p wa -k system-locale
+-w /etc/sysconfig/network -p wa -k system-locale
+-w /etc/sysconfig/network-scripts/ -p wa -k system-locale
+" >> /etc/audit/rules.d/50-system_locale.rules +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure use of privileged commands are collected + + +Monitor privileged programs, those that have the setuid + and/or setgid + bit set on execution, to determine if unprivileged users are running these commands. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Execution of privileged commands by non-privileged users could be an indication of someone trying to gain unauthorized access to the system. + + + + NIST SP 800-53 Rev. 5: AU-3, AU-3(1) + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor the use of privileged commands. + Example: + +# {
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ AUDIT_RULE_FILE="/etc/audit/rules.d/50-privileged.rules"
+ NEW_DATA=()
+ for PARTITION in $(findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) | grep -Pv "noexec|nosuid" | awk '{print $1}'); do
+ readarray -t DATA < <(find "${PARTITION}" -xdev -perm /6000 -type f | awk -v UID_MIN=${UID_MIN} '{print "-a always,exit -F path=" $1 " -F perm=x -F auid>="UID_MIN" -F auid!=unset -k privileged" }')
+ for ENTRY in "${DATA[@]}"; do
+ NEW_DATA+=("${ENTRY}")
+ done
+ done
+ readarray &> /dev/null -t OLD_DATA < "${AUDIT_RULE_FILE}"
+ COMBINED_DATA=( "${OLD_DATA[@]}" "${NEW_DATA[@]}" )
+ printf '%s\n' "${COMBINED_DATA[@]}" | sort -u > "${AUDIT_RULE_FILE}"
+} +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + Special mount points + +If there are any special mount points that are not visible by default from just scanning / +, change the PARTITION + variable to the appropriate partition and re-run the remediation. + Impact: + + +Both the audit and remediation section of this recommendation will traverse all mounted file systems that is not mounted with either noexec + or nosuid + mount options. If there are large file systems without these mount options, such traversal will be significantly detrimental to the performance of the system. + + Before running either the audit or remediation section, inspect the output of the following command to determine exactly which file systems will be traversed: + # findmnt -n -l -k -it $(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) | grep -Pv "noexec|nosuid" + + +To exclude a particular file system due to adverse performance impacts, update the audit and remediation sections by adding a sufficiently unique string to the grep + statement. The above command can be used to test the modified exclusions. + +
+
+
+ + + + + + + + + + +
+ + Ensure unsuccessful file access attempts are collected + + Monitor for unsuccessful attempts to access files. The following parameters are associated with system calls that control files: + + +creation - creat + + +opening - open + , openat + + +truncation - truncate + , ftruncate + + + An audit log record will only be written if all of the following criteria is met for the user when trying to access a file: + + a non-privileged user (auid>=UID_MIN) + is not a Daemon event (auid=4294967295/unset/-1) + if the system call returned EACCES (permission denied) or EPERM (some other permanent error associated with the specific system call) + + + + + + + + Network + Detect + + + + + + Data + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Failed attempts to open, create or truncate files could be an indication that an individual or process is trying to gain unauthorized access to the system. + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor unsuccessful file access attempts. + 64 Bit systems + Example: + +# {
+UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+[ -n "${UID_MIN}" ] && printf "
+-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>=${UID_MIN} -F auid!=unset -k access
+-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>=${UID_MIN} -F auid!=unset -k access
+-a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EACCES -F auid>=${UID_MIN} -F auid!=unset -k access
+-a always,exit -F arch=b32 -S creat,open,openat,truncate,ftruncate -F exit=-EPERM -F auid>=${UID_MIN} -F auid!=unset -k access
+" >> /etc/audit/rules.d/50-access.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure events that modify user/group information are collected + + Record events affecting the modification of user or group information, including that of passwords and old passwords if in use. + + + /etc/group + - system groups + + /etc/passwd + - system users + + /etc/gshadow + - encrypted password for each group + + /etc/shadow + - system user passwords + + /etc/security/opasswd + - storage of old passwords if the relevant PAM module is in use + + The parameters in this section will watch the files to see if they have been opened for write or have had attribute changes (e.g. permissions) and tag them with the identifier "identity" in the audit log file. + + + + + + + Network + Detect + + + + + + Users + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Unexpected changes to these files could be an indication that the system has been compromised and that an unauthorized user is attempting to hide their activities or compromise additional accounts. + + + + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor events that modify user/group information. + Example: + +# printf "
+-w /etc/group -p wa -k identity
+-w /etc/passwd -p wa -k identity
+-w /etc/gshadow -p wa -k identity
+-w /etc/shadow -p wa -k identity
+-w /etc/security/opasswd -p wa -k identity
+" >> /etc/audit/rules.d/50-identity.rules +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure discretionary access control permission modification events are collected + + Monitor changes to file permissions, attributes, ownership and group. The parameters in this section track changes for system calls that affect file permissions and attributes. The following commands and system calls effect the permissions, ownership and various attributes of files. + + + chmod + + + fchmod + + + fchmodat + + + chown + + + fchown + + + fchownat + + + lchown + + + setxattr + + + lsetxattr + + + fsetxattr + + + removexattr + + + lremovexattr + + + fremovexattr + + + In all cases, an audit record will only be written for non-system user ids and will ignore Daemon events. All audit records will be tagged with the identifier "perm_mod." + + + + + + + Network + Detect + + + + + + Applications + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Monitoring for changes in file attributes could alert a system administrator to activity that could indicate intruder activity or policy violation. + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor discretionary access control permission modification events. + 64 Bit systems + Example: + +# {
+UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+[ -n "${UID_MIN}" ] && printf "
+-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+-a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=${UID_MIN} -F auid!=unset -F key=perm_mod
+" >> /etc/audit/rules.d/50-perm_mod.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure successful file system mounts are collected + + +Monitor the use of the mount + system call. The mount + (and umount + ) system call controls the mounting and unmounting of file systems. The parameters below configure the system to create an audit record when the mount system call is used by a non-privileged user + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +It is highly unusual for a non privileged user to mount + file systems to the system. While tracking mount + commands gives the system administrator evidence that external media may have been mounted (based on a review of the source of the mount and confirming it's an external media type), it does not conclusively indicate that data was exported to the media. System administrators who wish to determine if data were exported, would also have to track successful open +, creat + and truncate + system calls requiring write access to a file under the mount point of the external media file system. This could give a fair indication that a write occurred. The only way to truly prove it, would be to track successful writes to the external media. Tracking write system calls could quickly fill up the audit log and is not recommended. Recommendations on configuration options to track data export to media is beyond the scope of this document. + + + + NIST SP 800-53 Rev. 5: CM-6 + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor successful file system mounts. + 64 Bit systems + Example: + +# {
+UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+[ -n "${UID_MIN}" ] && printf "
+-a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=unset -k mounts
+-a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=unset -k mounts
+" >> /etc/audit/rules.d/50-mounts.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. +
+
+
+ + + + + + + + + + + + +
+ + Ensure session initiation information is collected + + Monitor session initiation events. The parameters in this section track changes to the files associated with session events. + + + /var/run/utmp + - tracks all currently logged in users. + + /var/log/wtmp + - file tracks logins, logouts, shutdown, and reboot events. + + /var/log/btmp + - keeps track of failed login attempts and can be read by entering the command /usr/bin/last -f /var/log/btmp +. + + All audit records will be tagged with the identifier "session." + + + + + + + Network + Detect + + + + + + Users + Detect + + + + Users + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Monitoring these files for changes could alert a system administrator to logins occurring at unusual hours, which could indicate intruder activity (i.e. a user logging in at a time when they do not normally log in). + + + + + NIST SP 800-53 Rev. 5: AU-3, AU-3(1) + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor session initiation information. + Example: + +# printf "
+-w /var/run/utmp -p wa -k session
+-w /var/log/wtmp -p wa -k session
+-w /var/log/btmp -p wa -k session
+" >> /etc/audit/rules.d/50-session.rules +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure login and logout events are collected + + Monitor login and logout events. The parameters below track changes to files associated with login/logout events. + + + /var/log/lastlog + - maintain records of the last time a user successfully logged in. + + /var/run/faillock + - directory maintains records of login failures via the pam_faillock + module. + + + + + + + + Network + Detect + + + + + + Users + Detect + + + + Users + Protect + + + + Users + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Monitoring login/logout events could provide a system administrator with information associated with brute force attacks against user logins. + + + + + + NIST SP 800-53 Rev. 5: AU-3, AU-3(1) + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor login and logout events. + Example: + +# printf "
+-w /var/log/lastlog -p wa -k logins
+-w /var/run/faillock -p wa -k logins
+" >> /etc/audit/rules.d/50-login.rules +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + +
+ + Ensure file deletion events by users are collected + + Monitor the use of system calls associated with the deletion or renaming of files and file attributes. This configuration statement sets up monitoring for: + + + unlink + - remove a file + + unlinkat + - remove a file attribute + + rename + - rename a file + + renameat + rename a file attribute +system calls and tags them with the identifier "delete". + + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Monitoring these calls from non-privileged users could provide a system administrator with evidence that inappropriate removal of files and file attributes associated with protected files is occurring. While this audit option will look at all events, system administrators will want to look for specific privileged files that are being deleted or altered. + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor file deletion events by users. + 64 Bit systems + Example: + +# {
+UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+[ -n "${UID_MIN}" ] && printf "
+-a always,exit -F arch=b64 -S rename,unlink,unlinkat,renameat -F auid>=${UID_MIN} -F auid!=unset -F key=delete
+-a always,exit -F arch=b32 -S rename,unlink,unlinkat,renameat -F auid>=${UID_MIN} -F auid!=unset -F key=delete
+" >> /etc/audit/rules.d/50-delete.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. +
+
+
+ + + + + + + + + + + + +
+ + Ensure events that modify the system's Mandatory Access Controls are collected + + +Monitor SELinux, an implementation of mandatory access controls. The parameters below monitor any write access (potential additional, deletion or modification of files in the directory) or attribute changes to the /etc/selinux/ + and /usr/share/selinux/ + directories. + + Note: + If a different Mandatory Access Control method is used, changes to the corresponding directories should be audited. + + + + + + + Network + Detect + + + + + + Applications + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + +Changes to files in the /etc/selinux/ + and /usr/share/selinux/ + directories could indicate that an unauthorized user is attempting to modify access controls and change security contexts, leading to a compromise of the system. + + + + + + + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor events that modify the system's Mandatory Access Controls. + Example: + +# printf "
+-w /etc/selinux -p wa -k MAC-policy
+-w /usr/share/selinux -p wa -k MAC-policy
+" >> /etc/audit/rules.d/50-MAC-policy.rules +
+ Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + +
+ + Ensure successful and unsuccessful attempts to use the chcon command are recorded + + +The operating system must generate audit records for successful/unsuccessful uses of the chcon + command. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. + Audit records can be generated from various components within the information system (e.g., module or policy filter). + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor successful and unsuccessful attempts to use the chcon + command. + 64 Bit systems + Example: + +# {
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ [ -n "${UID_MIN}" ] && printf "
+-a always,exit -F path=/usr/bin/chcon -F perm=x -F auid>=${UID_MIN} -F auid!=unset -k perm_chng
+" >> /etc/audit/rules.d/50-perm_chng.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. +
+
+
+ + + + + + + +
+ + Ensure successful and unsuccessful attempts to use the setfacl command are recorded + + +The operating system must generate audit records for successful/unsuccessful uses of the setfacl + command + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. + Audit records can be generated from various components within the information system (e.g., module or policy filter). + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor successful and unsuccessful attempts to use the setfacl + command. + 64 Bit systems + Example: + +# {
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ [ -n "${UID_MIN}" ] && printf "
+-a always,exit -F path=/usr/bin/setfacl -F perm=x -F auid>=${UID_MIN} -F auid!=unset -k perm_chng
+" >> /etc/audit/rules.d/50-perm_chng.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. +
+
+
+ + + + + + + +
+ + Ensure successful and unsuccessful attempts to use the chacl command are recorded + + +The operating system must generate audit records for successful/unsuccessful uses of the chacl + command + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. + Audit records can be generated from various components within the information system (e.g., module or policy filter). + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor successful and unsuccessful attempts to use the chacl + command. + 64 Bit systems + Example: + +# {
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ [ -n "${UID_MIN}" ] && printf "
+-a always,exit -F path=/usr/bin/chacl -F perm=x -F auid>=${UID_MIN} -F auid!=unset -k perm_chng
+" >> /etc/audit/rules.d/50-perm_chng.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. +
+
+
+ + + + + + + +
+ + Ensure successful and unsuccessful attempts to use the usermod command are recorded + + +The operating system must generate audit records for successful/unsuccessful uses of the usermod + command. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Without generating audit records that are specific to the security and mission needs of the organization, it would be difficult to establish, correlate, and investigate the events relating to an incident or identify those responsible for one. + Audit records can be generated from various components within the information system (e.g., module or policy filter). + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor successful and unsuccessful attempts to use the usermod + command. + 64 Bit systems + Example: + +# {
+ UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+ [ -n "${UID_MIN}" ] && printf "
+-a always,exit -F path=/usr/sbin/usermod -F perm=x -F auid>=${UID_MIN} -F auid!=unset -k usermod
+" >> /etc/audit/rules.d/50-usermod.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + 32 Bit systems + +Follow the same procedures as for 64 bit systems and ignore any entries with b64 +. +
+
+
+ + + + + + + +
+ + Ensure kernel module loading unloading and modification is collected + + +Monitor the loading and unloading of kernel modules. All the loading / listing / dependency checking of modules is done by kmod + via symbolic links. + The following system calls control loading and unloading of modules: + + + init_module + - load a module + + finit_module + - load a module (used when the overhead of using cryptographically signed modules to determine the authenticity of a module can be avoided) + + delete_module + - delete a module + + create_module + - create a loadable module entry + + query_module + - query the kernel for various bits pertaining to modules + + +Any execution of the loading and unloading module programs and system calls will trigger an audit record with an identifier of modules +. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + System call structure + +For performance ( man 7 audit.rules +) reasons it is preferable to have all the system calls on one line. However, your configuration may have them on one line each or some other combination. This is important to understand for both the auditing and remediation sections as the examples given are optimized for performance as per the man page. + + + + Monitoring the use of all the various ways to manipulate kernel modules could provide system administrators with evidence that an unauthorized change was made to a kernel module, possibly compromising the security of the system. + + + + + + + Create audit rules + +Edit or create a file in the /etc/audit/rules.d/ + directory, ending in .rules + extension, with the relevant rules to monitor kernel module modification. + 64 Bit systems + Example: + +# {
+UID_MIN=$(awk '/^\s*UID_MIN/{print $2}' /etc/login.defs)
+[ -n "${UID_MIN}" ] && printf "
+-a always,exit -F arch=b64 -S init_module,finit_module,delete_module,create_module,query_module -F auid>=${UID_MIN} -F auid!=unset -k kernel_modules
+-a always,exit -F path=/usr/bin/kmod -F perm=x -F auid>=${UID_MIN} -F auid!=unset -k kernel_modules
+" >> /etc/audit/rules.d/50-kernel_modules.rules || printf "ERROR: Variable 'UID_MIN' is unset.\n"
+} +
+ Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure the audit configuration is immutable + + +Set system audit so that audit rules cannot be modified with auditctl + . Setting the flag "-e 2" forces audit to be put in immutable mode. Audit changes can only be made on system reboot. + + Note: + This setting will require the system to be rebooted to update the active auditd + configuration settings. + + + + + + + Data + Protect + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + In immutable mode, unauthorized users cannot execute changes to the audit system to potentially hide malicious activity and then put the audit rules back. Users would most likely notice a system reboot and that could alert administrators of an attempt to make unauthorized audit changes. + + + + + + NIST SP 800-53 Rev. 5: AC-3, AU-3, AU-3(1), MP-2 + + + + +Edit or create the file /etc/audit/rules.d/99-finalize.rules + and add the line -e 2 + at the end of the file: + + Example: + + # printf '%s\n' "-e 2" > /etc/audit/rules.d/99-finalize.rules + + Load audit rules + Merge and load the rules into active configuration: + # augenrules --load + + Check if reboot is required. + # if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then printf "Reboot required to load rules\n"; fi + + + + + + + + + + + + + + Ensure the running and on disk configuration is the same + + The Audit system have both on disk and running configuration. It is possible for these configuration settings to differ. + + Note: + Due to the limitations of augenrules + and auditctl +, it is not absolutely guaranteed that loading the rule sets via augenrules --load + will result in all rules being loaded or even that the user will be informed if there was a problem loading the rules. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + + Potential reboot required + +If the auditing configuration is locked ( -e 2 +), then augenrules + will not warn in any way that rules could not be loaded into the running configuration. A system reboot will be required to load the rules into the running configuration. + + + + Configuration differences between what is currently running and what is on disk could cause unexpected problems or may give a false impression of compliance requirements. + + + + + + + If the rules are not aligned across all three () areas, run the following command to merge and load all rules: + # augenrules --load + + Check if reboot is required. + if [[ $(auditctl -s | grep "enabled") =~ "2" ]]; then echo "Reboot required to load rules"; fi + + + + + +
+ + Configure auditd file access + + Without the capability to restrict which roles and individuals can select which events are audited, unauthorized personnel may be able to prevent the auditing of critical events. + + + Ensure the audit log directory is 0750 or more restrictive + + The audit log directory contains audit log files. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Audit information includes all information including: audit records, audit settings and audit reports. This information is needed to successfully audit system activity. This information must be protected from unauthorized modification or deletion. If this information were to be compromised, forensic analysis and discovery of the true source of potentially malicious system activity is impossible to achieve. + + + + + + + Run the following command to configure the audit log directory to have a mode of "0750" or less permissive: + # chmod g-w,o-rwx "$(dirname $( awk -F"=" '/^\s*log_file\s*=\s*/ {print $2}' /etc/audit/auditd.conf))" + + + + + + + + + + + + + Ensure audit log files are mode 0640 or less permissive + + Audit log files contain information about the system and system activity. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to audit records can reveal system and configuration data to attackers, potentially compromising its confidentiality. + + + + + + + +Run the following command to remove more permissive mode than 0640 + from audit log files: + # [ -f /etc/audit/auditd.conf ] && find "$(dirname $(awk -F "=" '/^\s*log_file/ {print $2}' /etc/audit/auditd.conf | xargs))" -type f \( ! -perm 600 -a ! -perm 0400 -a ! -perm 0200 -a ! -perm 0000 -a ! -perm 0640 -a ! -perm 0440 -a ! -perm 0040 \) -exec chmod u-x,g-wx,o-rwx {} + + + + + + + + + + + + + + Ensure only authorized users own audit log files + + Audit log files contain information about the system and system activity. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to audit records can reveal system and configuration data to attackers, potentially compromising its confidentiality. + + + + + + + +Run the following command to configure the audit log files to be owned by the root + user: + # [ -f /etc/audit/auditd.conf ] && find "$(dirname $(awk -F "=" '/^\s*log_file/ {print $2}' /etc/audit/auditd.conf | xargs))" -type f ! -user root -exec chown root {} + + + + + + + + + + + + + + Ensure only authorized groups are assigned ownership of audit log files + + Audit log files contain information about the system and system activity. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to audit records can reveal system and configuration data to attackers, potentially compromising its confidentiality. + + + + + + + +Run the following command to configure the audit log files to be owned by root + group: + # find $(dirname $(awk -F"=" '/^\s*log_file\s*=\s*/ {print $2}' /etc/audit/auditd.conf | xargs)) -type f \( ! -group adm -a ! -group root \) -exec chgrp root {} + + + +Run the following command to configure the audit log files to be owned by the root + group: + # chgrp root /var/log/audit/ + + +Run the following command to set the log_group + parameter in the audit configuration file to log_group = root +: + # sed -ri 's/^\s*#?\s*log_group\s*=\s*\S+(\s*#.*)?.*$/log_group = root\1/' /etc/audit/auditd.conf + + Run the following command to restart the audit daemon to reload the configuration file: + # systemctl restart auditd + + + + + + + + + + + + + Ensure audit configuration files are 640 or more restrictive + + Audit configuration files control auditd and what events are audited. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to the audit configuration files could allow unauthorized personnel to prevent the auditing of critical events. + Misconfigured audit configuration files may prevent the auditing of critical events or impact the system's performance by overwhelming the audit log. Misconfiguration of the audit configuration files may also make it more difficult to establish and investigate events relating to an incident. + + + + + + + Run the following command to remove more permissive mode than 0640 from the audit configuration files: + # find /etc/audit/ -type f \( -name '*.conf' -o -name '*.rules' \) -exec chmod u-x,g-wx,o-rwx {} + + + + + + + + + + + + + Ensure audit configuration files are owned by root + + Audit configuration files control auditd and what events are audited. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to the audit configuration files could allow unauthorized personnel to prevent the auditing of critical events. + Misconfigured audit configuration files may prevent the auditing of critical events or impact the system's performance by overwhelming the audit log. Misconfiguration of the audit configuration files may also make it more difficult to establish and investigate events relating to an incident. + + + + + + + +Run the following command to change ownership to root + user: + # find /etc/audit/ -type f \( -name '*.conf' -o -name '*.rules' \) ! -user root -exec chown root {} + + + + + + + + + + + + + + + + + + + + Ensure audit configuration files belong to group root + + Audit configuration files control auditd and what events are audited. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Access to the audit configuration files could allow unauthorized personnel to prevent the auditing of critical events. + Misconfigured audit configuration files may prevent the auditing of critical events or impact the system's performance by overwhelming the audit log. Misconfiguration of the audit configuration files may also make it more difficult to establish and investigate events relating to an incident. + + + + + + + +Run the following command to change group to root +: + # find /etc/audit/ -type f \( -name '*.conf' -o -name '*.rules' \) ! -group root -exec chgrp root {} + + + + + + + + + + + + + Ensure audit tools are 755 or more restrictive + + Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Protecting audit information includes identifying and protecting the tools used to view and manipulate log data. Protecting audit tools is necessary to prevent unauthorized operation on audit information. + + + + + + + Run the following command to remove more permissive mode from the audit tools: + # chmod go-w /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/augenrules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure audit tools are owned by root + + Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Protecting audit information includes identifying and protecting the tools used to view and manipulate log data. Protecting audit tools is necessary to prevent unauthorized operation on audit information. + + + + + + + +Run the following command to change the owner of the audit tools to the root + user: + # chown root /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/augenrules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Ensure audit tools belong to group root + + Audit tools include, but are not limited to, vendor-provided and open source audit tools needed to successfully view and manipulate audit information system activity and records. Audit tools include custom queries and report generators. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Protecting audit information includes identifying and protecting the tools used to view and manipulate log data. Protecting audit tools is necessary to prevent unauthorized operation on audit information. + + + + + + + Run the following command to remove more permissive mode from the audit tools: + # chmod go-w /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/augenrules + + +Run the following command to change owner and group of the audit tools to root + user and group: + # chown root:root /sbin/auditctl /sbin/aureport /sbin/ausearch /sbin/autrace /sbin/auditd /sbin/augenrules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Configure Integrity Checking + + AIDE is a file integrity checking tool, similar in nature to Tripwire. While it cannot prevent intrusions, it can detect unauthorized changes to configuration files by alerting when the files are changed. When setting up AIDE, decide internally what the site policy will be concerning integrity checking. Review the AIDE quick start guide and AIDE documentation before proceeding. + + + Ensure AIDE is installed + + Advanced Intrusion Detection Environment (AIDE) is a intrusion detection tool that uses predefined rules to check the integrity of files and directories in the Linux operating system. AIDE has its own database to check the integrity of files and directories. + + aide + takes a snapshot of files and directories including modification times, permissions, and file hashes which can then be used to compare against the current state of the filesystem to detect modifications to the system. + + + + + + + Data + Detect + + + + + + Data + Detect + + + + + +The prelinking feature can interfere with aide + because it alters binaries to speed up their start up times. Run prelink -ua + to restore the binaries to their prelinked state, thus avoiding false positives from aide +. + + + + By monitoring the filesystem state compromised files can be detected to prevent or limit the exposure of accidental or malicious misconfigurations or modified binaries. + + + + AIDE stable manual: http://aide.sourceforge.net/stable/manual.html + NIST SP 800-53 Rev. 5: AU-2 + + + + +Run the following command to install aide +: + # yum install aide + + +Configure aide + as appropriate for your environment. Consult the aide + documentation for options. + +Initialize aide +: + Run the following commands: + # aide --init + + # mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz + + + + + + + + + + + + Ensure filesystem integrity is regularly checked + + Periodic checking of the filesystem integrity is needed to detect changes to the filesystem. + + + + + + + Data + Detect + + + + + + Data + Detect + + + + + The checking in this recommendation occurs every day at 5am. Alter the frequency and time of the checks in compliance with site policy. + + + + Periodic file checking allows the system administrator to determine on a regular basis if critical files have been changed in an unauthorized fashion. + + + + https://github.com/konstruktoid/hardening/blob/master/config/aidecheck.service + https://github.com/konstruktoid/hardening/blob/master/config/aidecheck.timer + NIST SP 800-53 Rev. 5: AU-2 + + + + + - IF - + cron + will be used to schedule and run aide check + Run the following command: + # crontab -u root -e + + Add the following line to the crontab: + 0 5 * * * /usr/sbin/aide --check + + + - OR - + + + - IF - + aidecheck.service + and aidecheck.timer + will be used to schedule and run aide check: + +Create or edit the file /etc/systemd/system/aidecheck.service + and add the following lines: + +[Unit]
+Description=Aide Check
+
+[Service]
+Type=simple
+ExecStart=/usr/sbin/aide --check
+
+[Install]
+WantedBy=multi-user.target +
+ +Create or edit the file /etc/systemd/system/aidecheck.timer + and add the following lines: + +[Unit]
+Description=Aide check every day at 5AM
+
+[Timer]
+OnCalendar=*-*-* 05:00:00
+Unit=aidecheck.service
+
+[Install]
+WantedBy=multi-user.target +
+ Run the following commands: + +# chown root:root /etc/systemd/system/aidecheck.*
+# chmod 0644 /etc/systemd/system/aidecheck.*
+
+# systemctl daemon-reload
+
+# systemctl enable aidecheck.service
+# systemctl --now enable aidecheck.timer +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + System Maintenance + + Recommendations in this section are intended as maintenance and are intended to be checked on a frequent basis to ensure system stability. Many recommendations do not have quick remediations and require investigation into the cause and best fix available and may indicate an attempted breach of system security. + + + System File Permissions + + This section provides guidance on securing aspects of system files and directories. + + + Ensure permissions on /etc/passwd are configured + + +The /etc/passwd + file contains user account information that is used by many system utilities and therefore must be readable for these utilities to operate. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that the /etc/passwd + file is protected from unauthorized write access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/passwd +: + +# chmod u-x,go-wx /etc/passwd
+# chown root:root /etc/passwd +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/passwd- are configured + + +The /etc/passwd- + file contains backup user account information. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that the /etc/passwd- + file is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/passwd- +: + +# chmod u-x,go-wx /etc/passwd-
+# chown root:root /etc/passwd- +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/group are configured + + +The /etc/group + file contains a list of all the valid groups defined in the system. The command below allows read/write access for root and read access for everyone else. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +The /etc/group + file needs to be protected from unauthorized changes by non-privileged users, but needs to be readable as this information is used with many non-privileged programs. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/group +: + +# chmod u-x,go-wx /etc/group
+# chown root:root /etc/group +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/group- are configured + + +The /etc/group- + file contains a backup list of all the valid groups defined in the system. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that the /etc/group- + file is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/group- +: + +# chmod u-x,go-wx /etc/group-
+# chown root:root /etc/group- +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/shadow are configured + + +The /etc/shadow + file is used to store the information about user accounts that is critical to the security of those accounts, such as the hashed password and other security information. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +If attackers can gain read access to the /etc/shadow + file, they can easily run a password cracking program against the hashed password to break it. Other security information that is stored in the /etc/shadow + file (such as expiration) could also be useful to subvert the user accounts. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/shadow +: + +# chown root:root /etc/shadow
+# chmod 0000 /etc/shadow +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/shadow- are configured + + +The /etc/shadow- + file is used to store backup information about user accounts that is critical to the security of those accounts, such as the hashed password and other security information. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that the /etc/shadow- + file is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/shadow- +: + +# chown root:root /etc/shadow-
+# chmod 0000 /etc/shadow- +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/gshadow are configured + + +The /etc/gshadow + file is used to store the information about groups that is critical to the security of those accounts, such as the hashed password and other security information. + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + +If attackers can gain read access to the /etc/gshadow + file, they can easily run a password cracking program against the hashed password to break it. Other security information that is stored in the /etc/gshadow + file (such as group administrators) could also be useful to subvert the group. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/gshadow +: + +# chown root:root /etc/gshadow
+# chmod 0000 /etc/gshadow +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/gshadow- are configured + + +The /etc/gshadow- + file is used to store backup information about groups that is critical to the security of those accounts, such as the hashed password and other security information. + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + +It is critical to ensure that the /etc/gshadow- + file is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to set mode, owner, and group on /etc/gshadow- +: + +# chown root:root /etc/gshadow-
+# chmod 0000 /etc/gshadow- +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/shells are configured + + + /etc/shells + is a text file which contains the full pathnames of valid login shells. This file is consulted by chsh + and available to be queried by other programs. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that the /etc/shells + file is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/shells +: + +# chmod u-x,go-wx /etc/shells
+# chown root:root /etc/shells +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+ + Ensure permissions on /etc/security/opasswd are configured + + + /etc/security/opasswd + and it's backup /etc/security/opasswd.old + hold user's previous passwords if pam_unix + or pam_pwhistory + is in use on the system + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + +It is critical to ensure that /etc/security/opasswd + is protected from unauthorized access. Although it is protected by default, the file permissions could be changed either inadvertently or through malicious actions. + + + + NIST SP 800-53 Rev. 5: AC-3, MP-2 + + + + +Run the following commands to remove excess permissions, set owner, and set group on /etc/security/opasswd + and /etc/security/opasswd.old + is they exist: + +# [ -e "/etc/security/opasswd" ] && chmod u-x,go-rwx /etc/security/opasswd
+# [ -e "/etc/security/opasswd" ] && chown root:root /etc/security/opasswd
+# [ -e "/etc/security/opasswd.old" ] && chmod u-x,go-rwx /etc/security/opasswd.old
+# [ -e "/etc/security/opasswd.old" ] && chown root:root /etc/security/opasswd.old +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Ensure world writable files and directories are secured + + +World writable files are the least secure. Data in world-writable files can be modified and compromised by any user on the system. World writable files may also indicate an incorrectly written script or program that could potentially be the cause of a larger compromise to the system's integrity. See the chmod(2) + man page for more information. + Setting the sticky bit on world writable directories prevents users from deleting or renaming files in that directory that are not owned by them. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Data in world-writable files can be modified and compromised by any user on the system. World writable files may also indicate an incorrectly written script or program that could potentially be the cause of a larger compromise to the system's integrity. + +This feature prevents the ability to delete or rename files in world writable directories (such as /tmp + ) that are owned by another user. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + + +World Writable Files: + + +It is recommended that write access is removed from other + with the command ( chmod o-w <filename> + ), but always consult relevant vendor documentation to avoid breaking any application dependencies on a given file. + + + +World Writable Directories: + + +Set the sticky bit on all world writable directories with the command ( chmod a+t <directory_name> + ) + + + + Run the following script to: + + Remove other write permission from any world writable files + Add the sticky bit to all world writable directories + + +#!/usr/bin/env bash
+
+{
+ l_smask='01000'
+ a_path=(); a_arr=() # Initialize array
+ a_path=(! -path "/run/user/*" -a ! -path "/proc/*" -a ! -path "*/containerd/*" -a ! -path "*/kubelet/pods/*" -a ! -path "/sys/kernel/security/apparmor/*" -a ! -path "/snap/*" -a ! -path "/sys/fs/cgroup/memory/*" -a ! -path "/sys/fs/selinux/*")
+ while read -r l_bfs; do
+ a_path+=( -a ! -path ""$l_bfs"/*")
+ done < <(findmnt -Dkerno fstype,target | awk '$1 ~ /^\s*(nfs|proc|smb)/ {print $2}')
+ # Populate array with files
+ while IFS= read -r -d $'\0' l_file; do
+ [ -e "$l_file" ] && a_arr+=("$(stat -Lc '%n^%#a' "$l_file")")
+ done < <(find / \( "${a_path[@]}" \) \( -type f -o -type d \) -perm -0002 -print0 2>/dev/null)
+ while IFS="^" read -r l_fname l_mode; do # Test files in the array
+ if [ -f "$l_fname" ]; then # Remove excess permissions from WW files
+ echo -e " - File: \"$l_fname\" is mode: \"$l_mode\"\n - removing write permission on \"$l_fname\" from \"other\""
+ chmod o-w "$l_fname"
+ fi
+ if [ -d "$l_fname" ]; then
+ if [ ! $(( $l_mode & $l_smask )) -gt 0 ]; then # Add sticky bit
+ echo -e " - Directory: \"$l_fname\" is mode: \"$l_mode\" and doesn't have the sticky bit set\n - Adding the sticky bit"
+ chmod a+t "$l_fname"
+ fi
+ fi
+ done < <(printf '%s\n' "${a_arr[@]}")
+ unset a_path; unset a_arr # Remove array
+} +
+
+
+
+ + + + + + +
+ + Ensure no unowned or ungrouped files or directories exist + + Administrators may delete users or groups from the system and neglect to remove all files and/or directories owned by those users or groups. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + A new user or group who is assigned a deleted user's user ID or group ID may then end up "owning" a deleted user or group's files, and thus have more access on the system than was intended. + + + + NIST SP 800-53 Rev. 5: AC-3. MP-2 + + + + Remove or set ownership and group ownership of these files and/or directories to an active user on the system as appropriate. + + + + + + + + + + + + Ensure SUID and SGID files are reviewed + + The owner of a file can set the file's permissions to run with the owner's or group's permissions, even if the user running the program is not the owner or a member of the group. The most common reason for a SUID or SGID program is to enable users to perform functions (such as changing their password) that require root privileges. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + There are valid reasons for SUID and SGID programs, but it is important to identify and review such programs to ensure they are legitimate. Review the files returned by the action in the audit section and check to see if system binaries have a different checksum than what from the package. This is an indication that the binary may have been replaced. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5, AC-3, MP-2 + + + + Ensure that no rogue SUID or SGID programs have been introduced into the system. Review the files returned by the action in the Audit section and confirm the integrity of these binaries. + + + + + + Audit system file permissions + + +The RPM Package Manager + has a number of useful options. One of these, the -V + for RPM option, can be used to verify that system packages are correctly installed. The -V + option can be used to verify a particular package or to verify all system packages. If no output is returned, the package is installed correctly. The following table describes the meaning of output from the verify option: + +Code Meaning
+S File size differs.
+M File mode differs (includes permissions and file type).
+5 digest (formerly MD5 sum) differs.
+D Device file major/minor number mismatch.
+L readLink(2) path mismatch.
+U User ownership differs.
+G Group ownership differs.
+T The file time (mtime) differs.
+P Capabilities differ. +
+ +The rpm -qf + command can be used to determine which package a particular file belongs to. For example, the following commands determines which package the /bin/bash + file belongs to: + +# rpm -qf /bin/bash
+bash-4.4.19-2.fc28.x86_64 +
+ +To verify the settings for the package that controls the /bin/bash + file, run the following: + +# rpm -V bash-4.4.19-2.fc28.x86_64
+.M....... /bin/bash
+# rpm --verify bash
+??5?????? c /etc/bash.bashrc +
+ +Note that you can feed the output of the rpm -qf + command to the rpm -V + command: + +# rpm -V `rpm -qf /etc/passwd`
+.M...... c /etc/passwd
+S.5....T c /etc/printcap +
+ +The rpm -qi + command can be used to display package information, including name, version, and description. Following example displays information for bash + package: + +# rpm -qi bash
+Name : bash
+Version : 4.4.19
+Release : 2.fc28
+Architecture: x86_64
+Install Date: Tue 15 Aug 2023 10:27:28 AM EDT
+Group : Unspecified
+Size : 6910653
+License : GPLv3+
+Signature : RSA/SHA256, Thu 15 Mar 2018 10:10:10 AM EDT, Key ID e08e7e629db62fb1
+Source RPM : bash-4.4.19-2.fc28.src.rpm
+Build Date : Thu 15 Mar 2018 10:01:10 AM EDT
+Build Host : buildhw-07.phx2.fedoraproject.org
+Relocations : (not relocatable)
+Packager : Fedora Project
+Vendor : Fedora Project
+URL : https://www.gnu.org/software/bash
+Bug URL : https://bugz.fedoraproject.org/bash
+Summary : The GNU Bourne Again shell
+Description :
+The GNU Bourne Again shell (Bash) is a shell or command language
+interpreter that is compatible with the Bourne shell (sh). Bash
+incorporates useful features from the Korn shell (ksh) and the C shell
+(csh). Most sh scripts can be run by bash without modification. +
+
+ + + + + + Data + Protect + + + + + + Data + Protect + + + + + Since packages and important files may change with new updates and releases, it is recommended to verify everything, not just a finite list of files. This can be a time consuming task and results may depend on site policy therefore it is not a scorable benchmark item, but is provided for those interested in additional security measures. + Some of the recommendations of this benchmark alter the state of files audited by this recommendation. The audit command will alert for all changes to a file's permissions even if the new state is more secure than the default. + + + + It is important to confirm that packaged system files and directories are maintained with the permissions they were intended to have from the OS vendor. + + + + https://docs.fedoraproject.org/en-US/quick-docs/package-management/ + RPM(8) + NIST SP 800-53 Rev. 5: AC-3, CM-1, CM-2, CM-6, CM-7, IA-5, MP-2 + + + + Correct any discrepancies found and rerun the audit until output is clean or risk is mitigated or accepted. + + + +
+
+ + Local User and Group Settings + + This section provides guidance on securing aspects of the local users and groups. + + Note: + The recommendations in this section check local users and groups. Any users or groups from other sources such as LDAP will not be audited. In a domain environment, similar checks should be performed against domain users and groups. + + + Ensure accounts in /etc/passwd use shadowed passwords + + +Local accounts can use shadowed passwords. With shadowed passwords, the passwords are saved in shadow password file, /etc/shadow +, encrypted by a salted one-way hash. Accounts with a shadowed password have an x + in the second field in /etc/passwd +. + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + +The pwconv + command creates shadow from passwd + and an optionally existing shadow +. + + +The pwunconv + command creates passwd + from passwd + and shadow + and then removes shadow +. + +The grpconv + command creates gshadow + from group + and an optionally existing gshadow +. + +The grpunconv + command creates group + from group + and gshadow + and then removes gshadow +. + + +These four programs all operate on the normal and shadow password and group files: /etc/passwd +, /etc/group +, /etc/shadow +, and /etc/gshadow +. + +Each program acquires the necessary locks before conversion. pwconv + and grpconv + are similar. First, entries in the shadowed file which don't exist in the main file are removed. Then, shadowed entries which don't have x' as the password in the main file are updated. Any missing shadowed entries are added. Finally, passwords in the main file are replaced with +x'. These programs can be used for initial conversion as well to update the shadowed file if the main file is edited by hand. + + pwconv + will use the values of PASS_MIN_DAYS, PASS_MAX_DAYS, and PASS_WARN_AGE from /etc/login.defs when adding new entries to /etc/shadow. + + pwunconv + and grpunconv + are similar. Passwords in the main file are updated from the shadowed file. Entries which exist in the main file but not in the shadowed file are left alone. Finally, the shadowed file is removed. Some password aging information is lost by pwunconv +. It will convert what it can. + + + + +The /etc/passwd + file also contains information like user ID's and group ID's that are used by many system programs. Therefore, the /etc/passwd + file must remain world readable. In spite of encoding the password with a randomly-generated one-way hash function, an attacker could still break the system if they got access to the /etc/passwd + file. This can be mitigated by using shadowed passwords, thus moving the passwords in the /etc/passwd + file to /etc/shadow +. The /etc/shadow + file is set so only root will be able to read and write. This helps mitigate the risk of an attacker gaining access to the encoded passwords with which to perform a dictionary attack. + + Note: + + + All accounts must have passwords or be locked to prevent the account from being used by an unauthorized user. + +A user account with an empty second field in /etc/passwd + allows the account to be logged into by providing only the username. + + + + + NIST SP 800-53 Rev. 5: IA-5 + PWCONV(8) + + + + +Run the following command to set accounts to use shadowed passwords and migrate passwords in /etc/passwd + to /etc/shadow +: + # pwconv + + Investigate to determine if the account is logged in and what it is being used for, to determine if it needs to be forced off. + + + + + + + + + + + Ensure /etc/shadow password fields are not empty + + An account with an empty password field means that anybody may log in as that user without providing a password. + + + + + + + Users + Protect + + + + + + Users + Protect + + + + + + All accounts must have passwords or be locked to prevent the account from being used by an unauthorized user. + + + + NIST SP 800-53 Rev. 5: IA-5 + + + + +If any accounts in the /etc/shadow + file do not have a password, run the following command to lock the account until it can be determined why it does not have a password: + # passwd -l <username> + + Also, check to see if the account is logged in and investigate what it is being used for to determine if it needs to be forced off. + + + + + + + + + + + + Ensure all groups in /etc/passwd exist in /etc/group + + +Over time, system administration errors and changes can lead to groups being defined in /etc/passwd + but not in /etc/group + . + + + +Groups defined in the /etc/passwd + file but not in the /etc/group + file pose a threat to system security since group permissions are not properly managed. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Analyze the output of the Audit step above and perform the appropriate action to correct any discrepancies found. + + + + + + + + + + + + Ensure no duplicate UIDs exist + + +Although the useradd + program will not let you create a duplicate User ID (UID), it is possible for an administrator to manually edit the /etc/passwd + file and change the UID field. + + + Users must be assigned unique UIDs for accountability and to ensure appropriate access protections. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Based on the results of the audit script, establish unique UIDs and review all files owned by the shared UIDs to determine which UID they are supposed to belong to. + + + + + + + + + + + + Ensure no duplicate GIDs exist + + +Although the groupadd + program will not let you create a duplicate Group ID (GID), it is possible for an administrator to manually edit the /etc/group + file and change the GID field. + + + + +You can also use the grpck + command to check for other inconsistencies in the /etc/group + file. + + + + User groups must be assigned unique GIDs for accountability and to ensure appropriate access protections. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Based on the results of the audit script, establish unique GIDs and review all files owned by the shared GID to determine which group they are supposed to belong to. + + + + + + + + + + + + Ensure no duplicate user names exist + + +Although the useradd + program will not let you create a duplicate user name, it is possible for an administrator to manually edit the /etc/passwd + file and change the username. + + + +If a user is assigned a duplicate user name, it will create and have access to files with the first UID for that username in /etc/passwd + . For example, if "test4" has a UID of 1000 and a subsequent "test4" entry has a UID of 2000, logging in as "test4" will use UID 1000. Effectively, the UID is shared, which is a security problem. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Based on the results of the audit script, establish unique user names for the users. File ownerships will automatically reflect the change as long as the users have unique UIDs. + + + + + + + + + + + + Ensure no duplicate group names exist + + +Although the groupadd + program will not let you create a duplicate group name, it is possible for an administrator to manually edit the /etc/group + file and change the group name. + + + +If a group is assigned a duplicate group name, it will create and have access to files with the first GID for that group in /etc/group + . Effectively, the GID is shared, which is a security problem. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Based on the results of the audit script, establish unique names for the user groups. File group ownerships will automatically reflect the change as long as the groups have unique GIDs. + + + + + + + + + + + + Ensure root path integrity + + +The root + user can execute any command on the system and could be fooled into executing programs unintentionally if the PATH + is not set correctly. + + + +Including the current working directory (.) or other writable directory in root +'s executable path makes it likely that an attacker can gain superuser access by forcing an administrator operating as root + to execute a Trojan horse program. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Correct or justify any: + + Locations that are not directories + +Empty directories ( :: +) + +Trailing ( : +) + +Current working directory ( . +) + +Non root + owned directories + +Directories that less restrictive than mode 0755 + + + + + + + + + + + + + + Ensure root is the only UID 0 account + + Any account with UID 0 has superuser privileges on the system. + + + +This access must be limited to only the default root + account and only from the system console. Administrative access must be through an unprivileged account using an approved mechanism as noted in Item 5.6 Ensure access to the su command is restricted. + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + +Remove any users other than root + with UID 0 + or assign them a new UID if appropriate. + + + + + + + + + + + Ensure local interactive user home directories are configured + + +The user home directory is space defined for the particular user to set local environment variables and to store personal files. While the system administrator can establish secure permissions for users' home directories, the users can easily override these. Users can be defined in /etc/passwd + without a home directory or with a home directory that does not actually exist. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Since the user is accountable for files stored in the user home directory, the user must be the owner of the directory. Group or world-writable user home directories may enable malicious users to steal or modify other users' data or to gain another user's system privileges. If the user's home directory does not exist or is unassigned, the user will be placed in "/" and will not be able to write any files or have local environment variables set. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + If a local interactive users' home directory is undefined and/or doesn't exist, follow local site policy and perform one of the following: + + Lock the user account + Remove the user from the system + +Create a directory for the user. If undefined, edit /etc/passwd + and add the absolute path to the directory to the last field of the user. + + Run the following script to: + + Remove excessive permissions from local interactive users home directories + Update the home directory's owner + + +#!/usr/bin/env bash
+
+{
+ l_output2=""
+ l_valid_shells="^($( awk -F\/ '$NF != "nologin" {print}' /etc/shells | sed -rn '/^\//{s,/,\\\\/,g;p}' | paste -s -d '|' - ))$"
+ unset a_uarr && a_uarr=() # Clear and initialize array
+ while read -r l_epu l_eph; do # Populate array with users and user home location
+ a_uarr+=("$l_epu $l_eph")
+ done <<< "$(awk -v pat="$l_valid_shells" -F: '$(NF) ~ pat { print $1 " " $(NF-1) }' /etc/passwd)"
+ l_asize="${#a_uarr[@]}" # Here if we want to look at number of users before proceeding
+ [ "$l_asize " -gt "10000" ] && echo -e "\n ** INFO **\n - \"$l_asize\" Local interactive users found on the system\n - This may be a long running process\n"
+ while read -r l_user l_home; do
+ if [ -d "$l_home" ]; then
+ l_mask='0027'
+ l_max="$( printf '%o' $(( 0777 & ~$l_mask)) )"
+ while read -r l_own l_mode; do
+ if [ "$l_user" != "$l_own" ]; then
+ l_output2="$l_output2\n - User: \"$l_user\" Home \"$l_home\" is owned by: \"$l_own\"\n - changing ownership to: \"$l_user\"\n"
+ chown "$l_user" "$l_home"
+ fi
+ if [ $(( $l_mode & $l_mask )) -gt 0 ]; then
+ l_output2="$l_output2\n - User: \"$l_user\" Home \"$l_home\" is mode: \"$l_mode\" should be mode: \"$l_max\" or more restrictive\n - removing excess permissions\n"
+ chmod g-w,o-rwx "$l_home"
+ fi
+ done <<< "$(stat -Lc '%U %#a' "$l_home")"
+ else
+ l_output2="$l_output2\n - User: \"$l_user\" Home \"$l_home\" Doesn't exist\n - Please create a home in accordance with local site policy"
+ fi
+ done <<< "$(printf '%s\n' "${a_uarr[@]}")"
+ if [ -z "$l_output2" ]; then # If l_output2 is empty, we pass
+ echo -e " - No modification needed to local interactive users home directories"
+ else
+ echo -e "\n$l_output2"
+ fi
+} +
+
+
+
+ + + + + + +
+ + Ensure local interactive user dot files access is configured + + While the system administrator can establish secure permissions for users' "dot" files, the users can easily override these. + + + .forward + file specifies an email address to forward the user's mail to. + + .rhost + file provides the "remote authentication" database for the rcp, rlogin, and rsh commands and the rcmd() function. These files bypass the standard password-based user authentication mechanism. They specify remote hosts and users that are considered trusted (i.e. are allowed to access the local system without supplying a password) + + .netrc + file contains data for logging into a remote host or passing authentication to an API. + + .bash_history + file keeps track of the user’s last 500 commands. + + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + User configuration files with excessive or incorrect access may enable malicious users to steal or modify other users' data or to gain another user's system privileges. + + + + NIST SP 800-53 Rev. 5: CM-1, CM-2, CM-6, CM-7, IA-5 + + + + Making global modifications to users' files without alerting the user community can result in unexpected outages and unhappy users. Therefore, it is recommended that a monitoring policy be established to report user dot file permissions and determine the action to be taken in accordance with site policy. + The following script will: + + +remove excessive permissions on dot + files within interactive users' home directories + +change ownership of dot + files within interactive users' home directories to the user + +change group ownership of dot + files within interactive users' home directories to the user's primary group + +list .forward + and .rhost + files to be investigated and manually deleted + + +#!/usr/bin/env bash
+
+{
+ l_valid_shells="^($( awk -F\/ '$NF != "nologin" {print}' /etc/shells | sed -rn '/^\//{s,/,\\\\/,g;p}' | paste -s -d '|' - ))$"
+ unset a_uarr && a_uarr=() # Clear and initialize array
+ while read -r l_epu l_eph; do # Populate array with users and user home location
+ [[ -n "$l_epu" && -n "$l_eph" ]] && a_uarr+=("$l_epu $l_eph")
+ done <<< "$(awk -v pat="$l_valid_shells" -F: '$(NF) ~ pat { print $1 " " $(NF-1) }' /etc/passwd)"
+ l_asize="${#a_uarr[@]}" # Here if we want to look at number of users before proceeding
+ l_maxsize="1000" # Maximum number of local interactive users before warning (Default 1,000)
+ [ "$l_asize " -gt "$l_maxsize" ] && echo -e "\n ** INFO **\n - \"$l_asize\" Local interactive users found on the system\n - This may be a long running check\n"
+ file_access_fix()
+ {
+ l_facout2=""
+ l_max="$( printf '%o' $(( 0777 & ~$l_mask)) )"
+ if [ $(( $l_mode & $l_mask )) -gt 0 ]; then
+ echo -e " - File: \"$l_hdfile\" is mode: \"$l_mode\" and should be mode: \"$l_max\" or more restrictive\n - Changing to mode \"$l_max\""
+ chmod "$l_chp" "$l_hdfile"
+ fi
+ if [[ ! "$l_owner" =~ ($l_user) ]]; then
+ echo -e " - File: \"$l_hdfile\" owned by: \"$l_owner\" and should be owned by \"${l_user//|/ or }\"\n - Changing ownership to \"$l_user\""
+ chown "$l_user" "$l_hdfile"
+ fi
+ if [[ ! "$l_gowner" =~ ($l_group) ]]; then
+ echo -e " - File: \"$l_hdfile\" group owned by: \"$l_gowner\" and should be group owned by \"${l_group//|/ or }\"\n - Changing group ownership to \"$l_group\""
+ chgrp "$l_group" "$l_hdfile"
+ fi
+ }
+ while read -r l_user l_home; do
+ if [ -d "$l_home" ]; then
+ echo -e "\n - Checking user: \"$l_user\" home directory: \"$l_home\""
+ l_group="$(id -gn "$l_user" | xargs)"
+ l_group="${l_group// /|}"
+ while IFS= read -r -d $'\0' l_hdfile; do
+ while read -r l_mode l_owner l_gowner; do
+ case "$(basename "$l_hdfile")" in
+ .forward | .rhost )
+ echo -e " - File: \"$l_hdfile\" exists\n - Please investigate and manually delete \"$l_hdfile\""
+ ;;
+ .netrc )
+ l_mask='0177'
+ l_chp="u-x,go-rwx"
+ file_access_fix ;;
+ .bash_history )
+ l_mask='0177'
+ l_chp="u-x,go-rwx"
+ file_access_fix ;;
+ * )
+ l_mask='0133'
+ l_chp="u-x,go-wx"
+ file_access_fix ;;
+ esac
+ done <<< "$(stat -Lc '%#a %U %G' "$l_hdfile")"
+ done < <(find "$l_home" -xdev -type f -name '.*' -print0)
+ fi
+ done <<< "$(printf '%s\n' "${a_uarr[@]}")"
+ unset a_uarr # Remove array
+} +
+
+
+
+ + + + + + +
+
+
+ + + + + + + + + + +IDxSj6PaKSU3rv+UgcYE7inhd0to5ojI9XXTLkDQEQw= + + + +TNf9eRy07ua5yM6tcbfCcFAuud6SIqKs3JTPWkvY3H13pYIMNtKL/UdMGNmCMh4awMlmUTVdMQrl +6QIDCj+BOUSj0kzsl2OsosDynWLEOfgsMfESUJfSVFG5y6XDmW77FCxLJLbsOo2bZnxZCo47ArLn +xAwNbrSlo7okv1X2VE+EfaTGoQI0KfNDiA4n4qFDtkIFORGLHAwAtH05rZi4EOXICSLGgexeRbUC +anHbFmJL5dteok4ltcmmYwhaYGLne+TSYnUt2/t3ZFfDBKgq9lZy2Y4/k4bSHGg9Ib+iF17Ig1kS +SHiYAT1jfXdNZNfOwgw82MH6bTlItKbTtJs4o6qA+KHLyJGPF7S311mfs12ikCwTGAkvWgD8IcLp +E/pxksUj7QIN4FveXk6wIpiptJNbMWEw/22Ovg6UmeQSGXd6YlFntqwmkacrhPuj33FzpS+h78Zr +JDoju00F/+YbZtxLIogc6qEUZkVJ3UyiecbvSpn+dRTbnFWMNiEt2t21 + + + + +MIIHbDCCBVSgAwIBAgIQCdceADCnzKkglkQGavVKYzANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQG +EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQg +RzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMB4XDTIzMTAxODAwMDAwMFoX +DTI2MTAxODIzNTk1OVowgfQxEzARBgsrBgEEAYI3PAIBAxMCVVMxGTAXBgsrBgEEAYI3PAIBAhMI +TWFyeWxhbmQxHTAbBgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRIwEAYDVQQFEwlEMDYwNTg4 +MTIxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhOZXcgWW9yazEXMBUGA1UEBxMORWFzdCBHcmVlbmJ1 +c2gxKjAoBgNVBAoTIUNlbnRlciBmb3IgSW50ZXJuZXQgU2VjdXJpdHksIEluYzEqMCgGA1UEAxMh +Q2VudGVyIGZvciBJbnRlcm5ldCBTZWN1cml0eSwgSW5jMIIBojANBgkqhkiG9w0BAQEFAAOCAY8A +MIIBigKCAYEAyZwTJr4Kb7QEDnVY3BEkPoS3fn+XoxhCTfFvlMk8zXDcR4pFMgPkXTrZ1KbENUIV +skIhy1vmnh61vwSL0lcdzesCekKSxLqrEA4xunZk1MB5mLbHXchSSpI1co1vaSzJjkTYP1UsUVQz +NoOzV0LNCoZdahjGTHSduWvbrHTEeD6/jvUj3AVWrTx6krzgYA3ozAxtUnapK7IrZERuGOSwTrgR +Lr1aieQdFy8haW1YL+ks5HmLRvcoGp+J68GX/zfMTduvWxhcpXm4txKc4iJqGCRmyBU2XjGjpDBX +ndxHgT4edRLz5PwWSeZUh8/tWOGFwnVw7njoOa0sFixy96H2BqEn+yPeRALyf59rjtPL66tsXuaA +ilB63yqbH42mnkxHTX5zcEm337GzTOGRccsYzN/ApVuXDeMfsO+sSUosrimnxm9QTOyKcex5h1As +rxBVfTw+Zf7jP1YRlmMlGPG1zffK2KjJhD6ivFi2sIDThEQLxIAC8XOjp2ZXvhx87yNTAgMBAAGj +ggICMIIB/jAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNVHQ4EFgQUX2/J9FT5 +5BNF7JqfAl9SJkv3pf0wPQYDVR0gBDYwNDAyBgVngQwBAzApMCcGCCsGAQUFBwIBFhtodHRwOi8v +d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMD +MIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRy +dXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8v +Y3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEz +ODQyMDIxQ0ExLmNybDCBlAYIKwYBBQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3Nw +LmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0Rp +Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0T +BAIwADANBgkqhkiG9w0BAQsFAAOCAgEASLOHf1FQ7TrGqEoVXYoeNSkRWdVCUGd1JCDs7Hb9sLd3 +eQuipZ4EorQS/9qMiVMhmlJfF75sNVQhr1K5UtjrDRFMTzYmh78hAyovowzgyVKdGFqiVVr5m+mb +vVyglLlA4V076LMBaKRknm1Dt8r0/5GSRCrkP2origpYMMaocN+iBX9+MImnh3J1Ehn0nRUhW86Q +mt3/YCVIaQZisv+KmOxyAq1m6fiIYIvXY+cH2dDIheoVteb/NjYnKVqE6xTpOsxF16pykXiM9yk3 +Q9nlJS36KEGRP4wrySU4eIBnTcv4mimZjkWj6bIvk8otNJ14FbyltaPwIc3dE3oPGfSmCrkxs73j +iqBs2TXLvblEmOhY8ko3xVdTm0zMP50MQVO948fz9yF+xdnPdMPT1/lCmGp6iWYRzxnvEkA1+HFl +yy9YztrM8WNAhXirpUZg3HWQE2ONLKMtqQrkIHD2nikda77flk6oirqDuCf8Q7g8s86/KabTdxrw +pRmsRjDkAfKVae58ctaKbmF32oU9BGk+9MPG8HF5Yfoh12DE1fLZcPzZrROrDFJGGxwhv5FlnxGg +6G7jKi68erZnpaOPayK0bXyZNunqkI9IbIuPqMw5qaCZZgN6AEHIbGTKdrWEf11DmR7MdTjv3/vu +JWtIXgk3kQFX27w9JhqV6QogVmzIfEQ= + + + + +yZwTJr4Kb7QEDnVY3BEkPoS3fn+XoxhCTfFvlMk8zXDcR4pFMgPkXTrZ1KbENUIVskIhy1vmnh61 +vwSL0lcdzesCekKSxLqrEA4xunZk1MB5mLbHXchSSpI1co1vaSzJjkTYP1UsUVQzNoOzV0LNCoZd +ahjGTHSduWvbrHTEeD6/jvUj3AVWrTx6krzgYA3ozAxtUnapK7IrZERuGOSwTrgRLr1aieQdFy8h +aW1YL+ks5HmLRvcoGp+J68GX/zfMTduvWxhcpXm4txKc4iJqGCRmyBU2XjGjpDBXndxHgT4edRLz +5PwWSeZUh8/tWOGFwnVw7njoOa0sFixy96H2BqEn+yPeRALyf59rjtPL66tsXuaAilB63yqbH42m +nkxHTX5zcEm337GzTOGRccsYzN/ApVuXDeMfsO+sSUosrimnxm9QTOyKcex5h1AsrxBVfTw+Zf7j +P1YRlmMlGPG1zffK2KjJhD6ivFi2sIDThEQLxIAC8XOjp2ZXvhx87yNT +AQAB + + + +
\ No newline at end of file diff --git a/test/sample_data/xccdf/cis/CIS_Apache_Tomcat_10_Benchmark_v1.1.0-xccdf.xml b/test/sample_data/xccdf/cis/CIS_Apache_Tomcat_10_Benchmark_v1.1.0-xccdf.xml new file mode 100644 index 000000000..b2d7dafbf --- /dev/null +++ b/test/sample_data/xccdf/cis/CIS_Apache_Tomcat_10_Benchmark_v1.1.0-xccdf.xml @@ -0,0 +1,3931 @@ + + draft + CIS Apache Tomcat 10 Benchmark + + +This document, Security Configuration Benchmark for Apache Tomcat 9, provides prescriptive guidance for establishing a secure configuration posture for Apache Tomcat versions 9 running on Linux. This guide was tested against Apache Tomcat 9 as installed by tar packages provided by Apache. To obtain the latest version of this guide, please visit http://benchmarks.cisecurity.org +. If you have questions, comments, or have identified ways to improve this guide, please write us at feedback@cisecurity.org +. + + BACKGROUND. + The Center for Internet Security ("CIS") provides benchmarks, scoring tools, software, data, information, suggestions, ideas, and other services and materials from the CIS website or elsewhere ("Products") as a public service to Internet users worldwide. Recommendations contained in the Products ("Recommendations") result from a consensus-building process that involves many security experts and are generally generic in nature. The Recommendations are intended to provide helpful information to organizations attempting to evaluate or improve the security of their networks, systems, and devices. Proper use of the Recommendations requires careful analysis and adaptation to specific user requirements. The Recommendations are not in any way intended to be a "quick fix" for anyone's information security needs. + NO REPRESENTATIONS, WARRANTIES, OR COVENANTS. + CIS makes no representations, warranties, or covenants whatsoever as to (i) the positive or negative effect of the Products or the Recommendations on the operation or the security of any particular network, computer system, network device, software, hardware, or any component of any of the foregoing or (ii) the accuracy, reliability, timeliness, or completeness of the Products or the Recommendations. CIS is providing the Products and the Recommendations "as is" and "as available" without representations, warranties, or covenants of any kind. USER AGREEMENTS. + By using the Products and/or the Recommendations, I and/or my organization ("We") agree and acknowledge that: + 1. No network, system, device, hardware, software, or component can be made fully secure; + 2. We are using the Products and the Recommendations solely at our own risk; + 3. We are not compensating CIS to assume any liabilities associated with our use of the Products or the Recommendations, even risks that result from CIS's negligence or failure to perform; + 4. We have the sole responsibility to evaluate the risks and benefits of the Products and Recommendations to us and to adapt the Products and the Recommendations to our particular circumstances and requirements; + 5. Neither CIS, nor any CIS Party (defined below) has any responsibility to make any corrections, updates, upgrades, or bug fixes; or to notify us of the need for any such corrections, updates, upgrades, or bug fixes; and + 6. Neither CIS nor any CIS Party has or will have any liability to us whatsoever (whether based in contract, tort, strict liability or otherwise) for any direct, indirect, incidental, consequential, or special damages (including without limitation loss of profits, loss of sales, loss of or damage to reputation,loss of customers, loss of software, data, information or emails, loss of privacy, loss of use of any computer or other equipment, business interruption, wasted management or other staff resources or claims of any kind against us from third parties) arising out of or in any way Connected with our use of or our inability to use any of the Products or Recommendations (even if CIS has been advised of the possibility of such damages), including without limitation any liability associated with infringement of intellectual property, defects, bugs, errors, omissions, viruses, worms, backdoors, Trojan horses or other harmful items. + GRANT OF LIMITED RIGHTS. + CIS hereby grants each user the following rights, but only so long as the user complies with all of the terms of these Agreed Terms of Use: + 1. Except to the extent that we may have received additional authorization pursuant to a written agreement with CIS, each user may download, install and use each of the Products on a single computer; + 2. Each user may print one or more copies of any Product or any component of a Product that is in a .txt, .pdf, .doc, .mcw, or .rtf format, provided that all such copies are printed in full and are kept intact, including without limitation the text of this Agreed Terms of Use in its entirety. + RETENTION OF INTELLECTUAL PROPERTY RIGHTS; LIMITATIONS ON DISTRIBUTION. + The Products are protected by copyright and other intellectual property laws and by international treaties. We acknowledge and agree that we are not acquiring title to any intellectual property rights in the Products and that full title and all ownership rights to the Products will remain the exclusive property of CIS or CIS Parties. CIS reserves all rights not expressly granted to users in the preceding section entitled "Grant of limited rights." + Subject to the paragraph entitled "Special Rules" (which includes a waiver, granted to some classes of CIS Members, of certain limitations in this paragraph), and except as we may have otherwise agreed in a written agreement with CIS, we agree that we will not (i) decompile, disassemble, reverse engineer, or otherwise attempt to derive the source code for any software Product that is not already in the form of source code; (ii) distribute, redistribute, encumber, sell, rent, lease, lend, sublicense, or otherwise transfer or exploit rights to any Product or any component of a Product; (iii) post any Product or any component of a Product on any website, bulletin board, ftp server, newsgroup, or other similar mechanism or device, without regard to whether such mechanism or device is internal or external, (iv) remove or alter trademark, logo, copyright or other proprietary notices, legends, symbols or labels in any Product or any component of a Product; (v) remove these Agreed Terms of Use from, or alter these Agreed Terms of Use as they appear in, any Product or any component of a Product; (vi) use any Product or any component of a Product with any derivative works based directly on a Product or any component of a Product; (vii) use any Product or any component of a Product with other products or applications that are directly and specifically dependent on such Product or any component for any part of their functionality, or (viii) represent or claim a particular level of compliance with a CIS Benchmark, scoring tool or other Product. We will not facilitate or otherwise aid other individuals or entities in any of the activities listed in this paragraph. + We hereby agree to indemnify, defend, and hold CIS and all of its officers, directors, members, contributors, employees, authors, developers, agents, affiliates, licensors, information and service providers, software suppliers, hardware suppliers, and all other persons who aided CIS in the creation, development, or maintenance of the Products or Recommendations ("CIS Parties") harmless from and against any and all liability, losses, costs, and expenses (including attorneys' fees and court costs) incurred by CIS or any CIS Party in connection with any claim arising out of any violation by us of the preceding paragraph, including without limitation CIS's right, at our expense, to assume the exclusive defense and control of any matter subject to this indemnification, and in such case, we agree to cooperate with CIS in its defense of such claim. We further agree that all CIS Parties are third-party beneficiaries of our undertakings in these Agreed Terms of Use. SPECIAL RULES. + CIS has created and will from time to time create, special rules for its members and for other persons and organizations with which CIS has a written contractual relationship. Those special rules will override and supersede these Agreed Terms of Use with respect to the users who are covered by the special rules. + CIS hereby grants each CIS Security Consulting or Software Vendor Member and each CIS Organizational User Member, but only so long as such Member remains in good standing with CIS and complies with all of the terms of these Agreed Terms of Use, the right to distribute the Products and Recommendations within such Member's own organization, whether by manual or electronic means. Each such Member acknowledges and agrees that the foregoing grant is subject to the terms of such Member's membership arrangement with CIS and may, therefore, be modified or terminated by CIS at any time. + CHOICE OF LAW; JURISDICTION; VENUE. + We acknowledge and agree that these Agreed Terms of Use will be governed by and construed in accordance with the laws of the State of Maryland, that any action at law or in equity arising out of or relating to these Agreed Terms of Use shall be filed only in the courts located in the State of Maryland, that we hereby consent and submit to the personal jurisdiction of such courts for the purposes of litigating any such action. If any of these Agreed Terms of Use shall be determined to be unlawful, void, or for any reason unenforceable, then such terms shall be deemed severable and shall not affect the validity and enforceability of any remaining provisions. + BY USING THE PRODUCTS I(WE) ACKNOWLEDGE THAT WE HAVE READ THESE AGREED TERMS OF USE IN THEIR ENTIRETY, UNDERSTAND THEM, AND I(WE) AGREE TO BE BOUND BY THEM IN ALL RESPECTS. + 1.1.0 + + Level 1 + + Items in this profile intend to: + + be practical and prudent; + provide a clear security benefit; and + not inhibit the utility of the technology beyond acceptable means. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level 2 + + This profile extends the "Level 1" profile. Items in this profile exhibit one or more of the following characteristics: + + are intended for environments or use cases where security is paramount + acts as defense in depth measure + may negatively inhibit the utility or performance of the technology + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/sample_data/xccdf/cis/CIS_Microsoft_IIS_10_Benchmark_v1.2.1-xccdf.xml b/test/sample_data/xccdf/cis/CIS_Microsoft_IIS_10_Benchmark_v1.2.1-xccdf.xml new file mode 100644 index 000000000..defa3d7f4 --- /dev/null +++ b/test/sample_data/xccdf/cis/CIS_Microsoft_IIS_10_Benchmark_v1.2.1-xccdf.xml @@ -0,0 +1,4405 @@ + + interim + CIS Microsoft IIS 10 Benchmark + + This document provides prescriptive guidance for establishing a secure configuration posture for Microsoft Windows IIS 10. + +This secure configuration guide is based on Microsoft Windows IIS 10 + and is intended for all versions of Microsoft Windows IIS 10 +. This secure configuration guide was tested on Microsoft Windows Server 2022 Datacenter +. + +To obtain the latest version of this secure configuration guide, please visit https://www.cisecurity.org/cis-benchmarks/ +. If you have questions, comments, or have identified ways to improve this guide, please write us at feedback@cisecurity.org +. + + BACKGROUND. + The Center for Internet Security ("CIS") provides benchmarks, scoring tools, software, data, information, suggestions, ideas, and other services and materials from the CIS website or elsewhere ("Products") as a public service to Internet users worldwide. Recommendations contained in the Products ("Recommendations") result from a consensus-building process that involves many security experts and are generally generic in nature. The Recommendations are intended to provide helpful information to organizations attempting to evaluate or improve the security of their networks, systems, and devices. Proper use of the Recommendations requires careful analysis and adaptation to specific user requirements. The Recommendations are not in any way intended to be a "quick fix" for anyone's information security needs. + NO REPRESENTATIONS, WARRANTIES, OR COVENANTS. + CIS makes no representations, warranties, or covenants whatsoever as to (i) the positive or negative effect of the Products or the Recommendations on the operation or the security of any particular network, computer system, network device, software, hardware, or any component of any of the foregoing or (ii) the accuracy, reliability, timeliness, or completeness of the Products or the Recommendations. CIS is providing the Products and the Recommendations "as is" and "as available" without representations, warranties, or covenants of any kind. USER AGREEMENTS. + By using the Products and/or the Recommendations, I and/or my organization ("We") agree and acknowledge that: + 1. No network, system, device, hardware, software, or component can be made fully secure; + 2. We are using the Products and the Recommendations solely at our own risk; + 3. We are not compensating CIS to assume any liabilities associated with our use of the Products or the Recommendations, even risks that result from CIS's negligence or failure to perform; + 4. We have the sole responsibility to evaluate the risks and benefits of the Products and Recommendations to us and to adapt the Products and the Recommendations to our particular circumstances and requirements; + 5. Neither CIS, nor any CIS Party (defined below) has any responsibility to make any corrections, updates, upgrades, or bug fixes; or to notify us of the need for any such corrections, updates, upgrades, or bug fixes; and + 6. Neither CIS nor any CIS Party has or will have any liability to us whatsoever (whether based in contract, tort, strict liability or otherwise) for any direct, indirect, incidental, consequential, or special damages (including without limitation loss of profits, loss of sales, loss of or damage to reputation,loss of customers, loss of software, data, information or emails, loss of privacy, loss of use of any computer or other equipment, business interruption, wasted management or other staff resources or claims of any kind against us from third parties) arising out of or in any way Connected with our use of or our inability to use any of the Products or Recommendations (even if CIS has been advised of the possibility of such damages), including without limitation any liability associated with infringement of intellectual property, defects, bugs, errors, omissions, viruses, worms, backdoors, Trojan horses or other harmful items. + GRANT OF LIMITED RIGHTS. + CIS hereby grants each user the following rights, but only so long as the user complies with all of the terms of these Agreed Terms of Use: + 1. Except to the extent that we may have received additional authorization pursuant to a written agreement with CIS, each user may download, install and use each of the Products on a single computer; + 2. Each user may print one or more copies of any Product or any component of a Product that is in a .txt, .pdf, .doc, .mcw, or .rtf format, provided that all such copies are printed in full and are kept intact, including without limitation the text of this Agreed Terms of Use in its entirety. + RETENTION OF INTELLECTUAL PROPERTY RIGHTS; LIMITATIONS ON DISTRIBUTION. + The Products are protected by copyright and other intellectual property laws and by international treaties. We acknowledge and agree that we are not acquiring title to any intellectual property rights in the Products and that full title and all ownership rights to the Products will remain the exclusive property of CIS or CIS Parties. CIS reserves all rights not expressly granted to users in the preceding section entitled "Grant of limited rights." + Subject to the paragraph entitled "Special Rules" (which includes a waiver, granted to some classes of CIS Members, of certain limitations in this paragraph), and except as we may have otherwise agreed in a written agreement with CIS, we agree that we will not (i) decompile, disassemble, reverse engineer, or otherwise attempt to derive the source code for any software Product that is not already in the form of source code; (ii) distribute, redistribute, encumber, sell, rent, lease, lend, sublicense, or otherwise transfer or exploit rights to any Product or any component of a Product; (iii) post any Product or any component of a Product on any website, bulletin board, ftp server, newsgroup, or other similar mechanism or device, without regard to whether such mechanism or device is internal or external, (iv) remove or alter trademark, logo, copyright or other proprietary notices, legends, symbols or labels in any Product or any component of a Product; (v) remove these Agreed Terms of Use from, or alter these Agreed Terms of Use as they appear in, any Product or any component of a Product; (vi) use any Product or any component of a Product with any derivative works based directly on a Product or any component of a Product; (vii) use any Product or any component of a Product with other products or applications that are directly and specifically dependent on such Product or any component for any part of their functionality, or (viii) represent or claim a particular level of compliance with a CIS Benchmark, scoring tool or other Product. We will not facilitate or otherwise aid other individuals or entities in any of the activities listed in this paragraph. + We hereby agree to indemnify, defend, and hold CIS and all of its officers, directors, members, contributors, employees, authors, developers, agents, affiliates, licensors, information and service providers, software suppliers, hardware suppliers, and all other persons who aided CIS in the creation, development, or maintenance of the Products or Recommendations ("CIS Parties") harmless from and against any and all liability, losses, costs, and expenses (including attorneys' fees and court costs) incurred by CIS or any CIS Party in connection with any claim arising out of any violation by us of the preceding paragraph, including without limitation CIS's right, at our expense, to assume the exclusive defense and control of any matter subject to this indemnification, and in such case, we agree to cooperate with CIS in its defense of such claim. We further agree that all CIS Parties are third-party beneficiaries of our undertakings in these Agreed Terms of Use. SPECIAL RULES. + CIS has created and will from time to time create, special rules for its members and for other persons and organizations with which CIS has a written contractual relationship. Those special rules will override and supersede these Agreed Terms of Use with respect to the users who are covered by the special rules. + CIS hereby grants each CIS Security Consulting or Software Vendor Member and each CIS Organizational User Member, but only so long as such Member remains in good standing with CIS and complies with all of the terms of these Agreed Terms of Use, the right to distribute the Products and Recommendations within such Member's own organization, whether by manual or electronic means. Each such Member acknowledges and agrees that the foregoing grant is subject to the terms of such Member's membership arrangement with CIS and may, therefore, be modified or terminated by CIS at any time. + CHOICE OF LAW; JURISDICTION; VENUE. + We acknowledge and agree that these Agreed Terms of Use will be governed by and construed in accordance with the laws of the State of Maryland, that any action at law or in equity arising out of or relating to these Agreed Terms of Use shall be filed only in the courts located in the State of Maryland, that we hereby consent and submit to the personal jurisdiction of such courts for the purposes of litigating any such action. If any of these Agreed Terms of Use shall be determined to be unlawful, void, or for any reason unenforceable, then such terms shall be deemed severable and shall not affect the validity and enforceability of any remaining provisions. + BY USING THE PRODUCTS I(WE) ACKNOWLEDGE THAT WE HAVE READ THESE AGREED TERMS OF USE IN THEIR ENTIRETY, UNDERSTAND THEM, AND I(WE) AGREE TO BE BOUND BY THEM IN ALL RESPECTS. + + 1.2.1 + + Level 1 - IIS 10 + + Items in this profile apply to Microsoft IIS 10 running on Microsoft Windows Server 2016 and intend to: + + be practical and prudent; + provide a clear security benefit; and + not inhibit the utility of the technology beyond acceptable means. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level 2 - IIS 10 + + This profile extends the "Level 1 - IIS 10" profile. Items in this profile apply to Microsoft IIS 10.0 running on Microsoft Windows Server 2016 and exhibit one or more of the following characteristics: + + are intended for environments or use cases where security is paramount; + acts as defense in depth measure; + may negatively inhibit the utility or performance of the technology. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (L1) Ensure 'Host headers' are on all sites + This value is used in Rule: (L1) Ensure 'Host headers' are on all sites + .+ + + + (L1) Ensure 'Directory browsing' is set to Disabled + This value is used in Rule: (L1) Ensure 'Directory browsing' is set to Disabled + false + + + (L1) Ensure 'application pool identity' is configured for anonymous user identity + This value is used in Rule: (L1) Ensure 'application pool identity' is configured for anonymous user identity + ^$ + + + (L2) Ensure 'maxURL request filter' is configured + This value is used in Rule: (L2) Ensure 'maxURL request filter' is configured + 4096 + + + (L2) Ensure 'MaxQueryString request filter' is configured + This value is used in Rule: (L2) Ensure 'MaxQueryString request filter' is configured + 2048 + + + (L2) Ensure non-ASCII characters in URLs are not allowed + This value is used in Rule: (L2) Ensure non-ASCII characters in URLs are not allowed + false + + + (L1) Ensure Double-Encoded requests will be rejected + This value is used in Rule: (L1) Ensure Double-Encoded requests will be rejected + false + + + (L1) Ensure Unlisted File Extensions are not allowed + This value is used in Rule: (L1) Ensure Unlisted File Extensions are not allowed + false + + + (L1) Ensure 'application pool identity' is configured for all application pools + This value is used in Rule: (L1) Ensure 'application pool identity' is configured for all application pools + ApplicationPoolIdentity + + + (L1) Ensure 'unique application pools' is set for sites + This value is used in Rule: (L1) Ensure 'unique application pools' is set for sites + 1 + + + (L1) Ensure 'forms authentication' require SSL + This value is used in Rule: (L1) Ensure 'forms authentication' require SSL + true + + + (L2) Ensure 'forms authentication' is set to use cookies + This value is used in Rule: (L2) Ensure 'forms authentication' is set to use cookies + UseCookies + + + (L1) Ensure 'cookie protection mode' is configured for forms authentication + This value is used in Rule: (L1) Ensure 'cookie protection mode' is configured for forms authentication + All + + + (L1) Ensure 'passwordFormat' is not set to clear + This value is used in Rule: (L1) Ensure 'passwordFormat' is not set to clear + Clear + + + (L2) Ensure 'credentials' are not stored in configuration files + This value is used in Rule: (L2) Ensure 'credentials' are not stored in configuration files + Clear + + + (L2) Ensure 'debug' is turned off + This value is used in Rule: (L2) Ensure 'debug' is turned off + false + + + (L2) Ensure custom error messages are not off + This value is used in Rule: (L2) Ensure custom error messages are not off + Off + + + (L1) Ensure IIS HTTP detailed errors are hidden from displaying remotely + This value is used in Rule: (L1) Ensure IIS HTTP detailed errors are hidden from displaying remotely + DetailedLocalOnly + + + (L2) Ensure ASP.NET stack tracing is not enabled + This value is used in Rule: (L2) Ensure ASP.NET stack tracing is not enabled + false + + + (L2) Ensure 'httpcookie' mode is configured for session state + This value is used in Rule: (L2) Ensure 'httpcookie' mode is configured for session state + UseCookies + + + (L1) Ensure 'cookies' are set with HttpOnly attribute + This value is used in Rule: (L1) Ensure 'cookies' are set with HttpOnly attribute + true + + + (L2) Ensure 'MachineKey validation method - .Net 3.5' is configured + This value is used in Rule: (L2) Ensure 'MachineKey validation method - .Net 3.5' is configured + (AES|SHA) + + + (L1) Ensure 'MachineKey validation method - .Net 4.5' is configured + This value is used in Rule: (L1) Ensure 'MachineKey validation method - .Net 4.5' is configured + (AES|SHA) + + + (L1) Ensure global .NET trust level is configured + This value is used in Rule: (L1) Ensure global .NET trust level is configured + ((?i)Medium|(?i)Low) + + + (L1) Ensure 'notListedIsapisAllowed' is set to false + This value is used in Rule: (L1) Ensure 'notListedIsapisAllowed' is set to false + false + + + (L1) Ensure 'notListedCgisAllowed' is set to false + This value is used in Rule: (L1) Ensure 'notListedCgisAllowed' is set to false + false + + + (L1) Ensure SSLv2 is Disabled + This value is used in Rule: (L1) Ensure SSLv2 is Disabled for the registry data type + reg_dword + + + (L1) Ensure SSLv2 is Disabled + This value is used in Rule: (L1) Ensure SSLv2 is Disabled for the registry value + 1 + + + (L1) Ensure SSLv2 is Disabled + This value is used in Rule: (L1) Ensure SSLv2 is Disabled for the registry data type + reg_dword + + + (L1) Ensure SSLv2 is Disabled + This value is used in Rule: (L1) Ensure SSLv2 is Disabled for the registry value + 0 + + + (L1) Ensure SSLv2 is Disabled + This value is used in Rule: (L1) Ensure SSLv2 is Disabled for the registry data type + reg_dword + + + (L1) Ensure SSLv2 is Disabled + This value is used in Rule: (L1) Ensure SSLv2 is Disabled for the registry value + 0 + + + (L1) Ensure SSLv2 is Disabled + This value is used in Rule: (L1) Ensure SSLv2 is Disabled for the registry data type + reg_dword + + + (L1) Ensure SSLv2 is Disabled + This value is used in Rule: (L1) Ensure SSLv2 is Disabled for the registry value + 1 + + + (L1) Ensure SSLv3 is Disabled + This value is used in Rule: (L1) Ensure SSLv3 is Disabled for the registry data type + reg_dword + + + (L1) Ensure SSLv3 is Disabled + This value is used in Rule: (L1) Ensure SSLv3 is Disabled for the registry value + 1 + + + (L1) Ensure SSLv3 is Disabled + This value is used in Rule: (L1) Ensure SSLv3 is Disabled for the registry data type + reg_dword + + + (L1) Ensure SSLv3 is Disabled + This value is used in Rule: (L1) Ensure SSLv3 is Disabled for the registry value + 0 + + + (L1) Ensure SSLv3 is Disabled + This value is used in Rule: (L1) Ensure SSLv3 is Disabled for the registry data type + reg_dword + + + (L1) Ensure SSLv3 is Disabled + This value is used in Rule: (L1) Ensure SSLv3 is Disabled for the registry value + 0 + + + (L1) Ensure SSLv3 is Disabled + This value is used in Rule: (L1) Ensure SSLv3 is Disabled for the registry data type + reg_dword + + + (L1) Ensure SSLv3 is Disabled + This value is used in Rule: (L1) Ensure SSLv3 is Disabled for the registry value + 1 + + + (L1) Ensure TLS 1.0 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.0 is Disabled for the registry data type + reg_dword + + + (L1) Ensure TLS 1.0 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.0 is Disabled for the registry value + 0 + + + (L1) Ensure TLS 1.0 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.0 is Disabled for the registry data type + reg_dword + + + (L1) Ensure TLS 1.0 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.0 is Disabled for the registry value + 1 + + + (L1) Ensure TLS 1.0 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.0 is Disabled for the registry data type + reg_dword + + + (L1) Ensure TLS 1.0 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.0 is Disabled for the registry value + 0 + + + (L1) Ensure TLS 1.0 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.0 is Disabled for the registry data type + reg_dword + + + (L1) Ensure TLS 1.0 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.0 is Disabled for the registry value + 1 + + + (L1) Ensure TLS 1.1 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.1 is Disabled for the registry data type + reg_dword + + + (L1) Ensure TLS 1.1 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.1 is Disabled for the registry value + 0 + + + (L1) Ensure TLS 1.1 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.1 is Disabled for the registry data type + reg_dword + + + (L1) Ensure TLS 1.1 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.1 is Disabled for the registry value + 1 + + + (L1) Ensure TLS 1.1 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.1 is Disabled for the registry data type + reg_dword + + + (L1) Ensure TLS 1.1 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.1 is Disabled for the registry value + 0 + + + (L1) Ensure TLS 1.1 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.1 is Disabled for the registry data type + reg_dword + + + (L1) Ensure TLS 1.1 is Disabled + This value is used in Rule: (L1) Ensure TLS 1.1 is Disabled for the registry value + 1 + + + (L1) Ensure TLS 1.2 is Enabled + This value is used in Rule: (L1) Ensure TLS 1.2 is Enabled for the registry data type + reg_dword + + + (L1) Ensure TLS 1.2 is Enabled + This value is used in Rule: (L1) Ensure TLS 1.2 is Enabled for the registry value + 1 + + + (L1) Ensure TLS 1.2 is Enabled + This value is used in Rule: (L1) Ensure TLS 1.2 is Enabled for the registry data type + reg_dword + + + (L1) Ensure TLS 1.2 is Enabled + This value is used in Rule: (L1) Ensure TLS 1.2 is Enabled for the registry value + 0 + + + (L1) Ensure NULL Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure NULL Cipher Suites is Disabled for the registry data type + reg_dword + + + (L1) Ensure NULL Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure NULL Cipher Suites is Disabled for the registry value + 0 + + + (L1) Ensure DES Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure DES Cipher Suites is Disabled for the registry data type + reg_dword + + + (L1) Ensure DES Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure DES Cipher Suites is Disabled for the registry value + 0 + + + (L1) Ensure RC4 Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure RC4 Cipher Suites is Disabled for the registry data type + reg_dword + + + (L1) Ensure RC4 Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure RC4 Cipher Suites is Disabled for the registry value + 0 + + + (L1) Ensure RC4 Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure RC4 Cipher Suites is Disabled for the registry data type + reg_dword + + + (L1) Ensure RC4 Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure RC4 Cipher Suites is Disabled for the registry value + 0 + + + (L1) Ensure RC4 Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure RC4 Cipher Suites is Disabled for the registry data type + reg_dword + + + (L1) Ensure RC4 Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure RC4 Cipher Suites is Disabled for the registry value + 0 + + + (L1) Ensure RC4 Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure RC4 Cipher Suites is Disabled for the registry data type + reg_dword + + + (L1) Ensure RC4 Cipher Suites is Disabled + This value is used in Rule: (L1) Ensure RC4 Cipher Suites is Disabled for the registry value + 0 + + + (L1) Ensure AES 128/128 Cipher Suite is Disabled + This value is used in Rule: (L1) Ensure AES 128/128 Cipher Suite is Disabled for the registry data type + reg_dword + + + (L1) Ensure AES 128/128 Cipher Suite is Disabled + This value is used in Rule: (L1) Ensure AES 128/128 Cipher Suite is Disabled for the registry value + 0 + + + (L1) Ensure AES 256/256 Cipher Suite is Enabled + This value is used in Rule: (L1) Ensure AES 256/256 Cipher Suite is Enabled for the registry data type + reg_dword + + + (L1) Ensure AES 256/256 Cipher Suite is Enabled + This value is used in Rule: (L1) Ensure AES 256/256 Cipher Suite is Enabled for the registry value + 1 + + + (L2) Ensure TLS Cipher Suite ordering is Configured + This value is used in Rule: (L2) Ensure TLS Cipher Suite ordering is Configured for the registry data type + reg_multi_sz + + + (L2) Ensure TLS Cipher Suite ordering is Configured + This value is used in Rule: (L2) Ensure TLS Cipher Suite ordering is Configured for the registry value + ^TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384|TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256|TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384|TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256|TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384|TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256|TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384|TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256$ + + + Basic Configurations + + This section contains basic Web server-level recommendations. + + + (L1) Ensure 'Web content' is on non-system partition + + Web resources published through IIS are mapped via Virtual Directories to physical locations on disk. It is recommended to map all Virtual Directories to a non-system disk volume. + + + + + + + + + + Isolating web content from system files may reduce the probability of web sites/applications exhausting system disk space. It can also reduce the file IO vulnerability in the web site/application from affecting the confidentiality and/or integrity of system files. + + + http://blogs.iis.net/thomad/archive/2008/02/10/moving-the-iis7-inetpub-directory-to-a-different-drive.aspx + + + + + +Browse to web content in C:\inetpub\wwwroot\ + + +Copy or cut content onto a dedicated and restricted web folder on a non-system drive such as D:\webroot\ + + Change mappings for any applications or Virtual Directories to reflect the new location + + +To change the mapping for the application named app1 + which resides under the Default Web Site, open IIS Manager: + + Expand the server node + Expand Sites + Expand Default Web Site + +Click on app1 + + In the Actions pane, select Basic Settings + +In the Physical path text box, put the new location of the application, D:\wwwroot\app1 + in the example above + + Impact: + + Once the configuration is changed all content from the root drive to the new drive including ACLs and empty directories will need to copied. + + + + + + + (L1) Ensure 'Host headers' are on all sites + + Host headers provide the ability to host multiple websites on the same IP address and port. It is recommended that host headers be configured for all sites. + + Note: + Wildcard host headers are now supported. + + + + + + + + + + Requiring a Host header for all sites may reduce the probability of DNS rebinding attacks successfully compromising or abusing site data or functionality and IP-based scans successfully identifying or interacting with a target application hosted on IIS. + + + http://technet.microsoft.com/en-us/library/cc753195%28WS.10%29.aspx + http://crypto.stanford.edu/dns/dns-rebinding.pdf + http://www.sslshopper.com/article-ssl-host-headers-in-iis-7.html + http://blogs.iis.net/thomad/archive/2008/01/25/ssl-certificates-on-sites-with-host-headers.aspx + https://www.iis.net/learn/get-started/whats-new-in-iis-10/wildcard-host-header-support + + + + +Obtain a listing of all sites by using the following appcmd.exe + command: + Enter the following command in AppCmd.exe to configure the host header: + %systemroot%\system32\inetsrv\appcmd.exe set config -section:system.applicationHost/sites /"[name='<website name>'].bindings.[protocol='http',bindingInformation='*:80:<host header>'].bindingInformation:"*:80:<host header>"" /commit:apphost + + + OR + + Enter the following command in PowerShell to configure the host header: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter 'system.applicationHost/sites/site[@name='<website name>']/bindings/binding[@protocol='http' and @bindingInformation='*:80:']' -name 'bindingInformation' -value '*:80:<host header value>' + + + OR + + Perform the following in IIS Manager to configure host headers for the Default Web Site: + + Open IIS Manager + In the Connections pane expand the Sites node and select Default Web Site + In the Actions pane click Bindings + In the Site Bindings dialog box, select the binding for which host headers are going to be configured, Port 80 in this example + Click Edit + +Under host name, enter the sites FQDN, such as <www.examplesite.com> + + Click OK, then Close + + + Note: + Requiring a host header may impair site functionality for HTTP/1.0 clients. + Impact: + + If a wildcard DNS entry exists and a wildcard host header is used, it may be serving data to more domains than intended. + + + + + + + + + + + + + (L1) Ensure 'Directory browsing' is set to Disabled + + Directory browsing allows the contents of a directory to be displayed upon request from a web client. If directory browsing is enabled for a directory in Internet Information Services, users receive a page that lists the contents of the directory when the following two conditions are met: + + No specific file is requested in the URL + The Default Documents feature is disabled in IIS, or if it is enabled, IIS is unable to locate a file in the directory that matches a name specified in the IIS default document list + + + Note: + If directory browsing is enabled (an exception to this recommendation), make sure that it is only enabled on the particular directory or directories that need to be shared. + + + + + + + Devices + Protect + + + + + + + + + Ensuring that directory browsing is disabled may reduce the probability of disclosing sensitive content that is inadvertently accessible via IIS. + + + + http://technet.microsoft.com/en-us/library/cc725840%28WS.10%29.aspx + http://technet.microsoft.com/en-us/library/cc731109%28WS.10%29.aspx + + + + +Directory Browsing can be set by using the UI, running appcmd.exe + commands, by editing configuration files directly, or by writing WMI scripts. To disable directory browsing at the server level using an appcmd.exe + command: + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config /section:directoryBrowse /enabled:false + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -Filter system.webserver/directorybrowse -PSPath iis:\ -Name Enabled -Value False + + Impact: + + Users will not be able to see the contents of directories. + + + + + + + + + + + + + (L1) Ensure 'application pool identity' is configured for all application pools + + +Application Pool Identities are the actual users/authorities that will run the worker process - w3wp.exe +. Assigning the correct user authority will help ensure that applications can function properly, while not giving overly permissive permissions on the system. These identities can further be used in ACLs to protect system content. It is recommended that each Application Pool run under a unique identity. + IIS has additional built-in least privilege identities intended for use by Application Pools. It is recommended that the default Application Pool Identity be changed to a least privilege principle other than Network Service. Furthermore, it is recommended that all application pool identities be assigned a unique least privilege principal. + To achieve isolation in IIS, application pools can be run as separate identities. IIS can be configured to automatically use the application pool identity if no anonymous user account is configured for a Web site. This can greatly reduce the number of accounts needed for Web sites and make management of the accounts easier. It is recommended the Application Pool Identity be set as the Anonymous User Identity. + +The name of the Application Pool account corresponds to the name of the Application Pool. Application Pool Identities were introduced in Windows Server 2008 SP2. It is recommended that Application Pools be set to run as ApplicationPoolIdentity + unless there is an underlying reason that the application pool needs to run as a specified end user account. One example where this is needed is for web farms using Kerberos authentication. + + + + + + + Data + Protect + + + + + + + + + +Setting Application Pools to use unique least privilege identities such as ApplicationPoolIdentity + reduces the potential harm the identity could cause should the application ever become compromised. + Additionally, it will simplify application pools configuration and account management. + + + + http://technet.microsoft.com/en-us/library/cc771170%28WS.10%29.aspx + http://learn.iis.net/page.aspx/140/understanding-built-in-user-and-group-accounts-in-iis-7/ + http://learn.iis.net/page.aspx/624/application-pool-identities/ + http://blogs.iis.net/tomwoolums/archive/2008/12/17/iis-7-0-application-pools.aspx + + + + +The default Application Pool identity may be set for an application using the IIS Manager GUI, using AppCmd.exe + commands in a command-line window, directly editing the configuration files, or by writing WMI scripts. Perform the following to change the default identity to the built-in ApplicationPoolIdentity + in the IIS Manager GUI: + + Open the IIS Manager GUI + In the connections pane, expand the server node and click Application Pools + +On the Application Pools page, select the DefaultAppPool +, and then click Advanced Settings in the Actions pane + +For the Identity property, click the '...' + button to open the Application Pool Identity dialog box + +Select the Built-in account option choose ApplicationPoolIdentity + from the list, or input a unique application user created for this purpose + Restart IIS + + +To change the ApplicationPool + identity to the built-in ApplicationPoolIdentity + using AppCmd.exe, run the following from a command prompt: + Enter the following command in AppCmd.exe to configure + %systemroot%\system32\inetsrv\appcmd set config /section:applicationPools /[name='<apppool name>'].processModel.identityType:ApplicationPoolIdentity + + OR + +To change the ApplicationPool + identity to the built-in ApplicationPoolIdentity + using PowerShell: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter 'system.applicationHost/applicationPools/add[@name='<apppool name>']/processModel' -name 'identityType' -value 'ApplicationPoolIdentity' + + +The example code above will set just the DefaultAppPool +. Run this command for each configured Application Pool. Additionally, ApplicationPoolIdentity + can be made the default for all Application Pools by using the Set Application Pool Defaults action on the Application Pools node. + If using a custom defined Windows user such as a dedicated service account, that user will need to be a member of the IIS_IUSRS group. The IIS_IUSRS group has access to all the necessary file and system resources so that an account, when added to this group, can seamlessly act as an application pool identity. + Impact: + + If Application Pool Identities are not set properly to users/authorities applications may not function properly. + + + + + + + + + + + + + (L1) Ensure 'unique application pools' is set for sites + + Application Pool Identities allows Application Pools to be run under unique accounts without the need to create and manage local or domain accounts. + It is recommended that all Sites run under unique, dedicated Application Pools. + + + + + + + Data + Protect + + + + + + By setting sites to run under unique Application Pools, resource-intensive applications can be assigned to their own application pools which could improve server and application performance. In addition, it can help maintain application availability: if an application in one pool fails, applications in other pools are not affected. Last, isolating applications helps mitigate the potential risk of one application being allowed access to the resources of another application. It is also recommended to stop any application pool that is not in use or was created by an installation such as .Net 4.0. + + + http://technet.microsoft.com/en-us/library/cc753449%28WS.10%29.aspx + http://blogs.iis.net/tomwoolums/archive/2008/12/17/iis-7-0-application-pools.aspx + http://learn.iis.net/page.aspx/624/application-pool-identities/ + + + + The following appcmd.exe command will set the application pool for a given application: + %systemroot%\system32\inetsrv\appcmd set app '<website name>/' /applicationpool:<apppool name> + + The output of this command will be similar to the following: APP object "Default Web Site/" changed (applicationPool:DefaultAppPool) + Run the above command to ensure a unique application pool is assigned for each site listed + OR + Enter the following command in PowerShell to configure: + Set-ItemProperty -Path 'IIS:\Sites\<website name>' -Name applicationPool -Value <apppool name> + + OR + + Open IIS Manager + Open the Sites node underneath the machine node + Select the Site to be changed + In the Actions pane, select Basic Settings + Click the Select… box next to the Application Pool text box + Select the desired Application Pool + Once selected, click OK + + Impact: + + All sites will need to be run under unique dedicated Application Pools. + + + + + + + + + + + + + (L1) Ensure 'application pool identity' is configured for anonymous user identity + + To achieve isolation in IIS, application pools can be run as separate identities. IIS can be configured to automatically use the application pool identity if no anonymous user account is configured for a web site. This can greatly reduce the number of accounts needed for Web sites and make management of the accounts easier. + It is recommended the Application Pool Identity be set as the Anonymous User Identity. + + + + + + + Data + Protect + + + + + + Configuring the anonymous user identity to use the application pool identity will help ensure site isolation - provided sites are set to use the application pool identity. Since a unique principal will run each application pool, it will ensure the identity is least privilege. Additionally, it will simplify Site management. + + + http://learn.iis.net/page.aspx/202/application-pool-identity-as-anonymous-user/ + http://learn.iis.net/page.aspx/624/application-pool-identities/ + + + + +The Anonymous User Identity can be set to Application Pool Identity by using the IIS Manager GUI, using AppCmd.exe + commands in a command-line window, directly editing the configuration files, or by writing WMI scripts. Perform the following to set the username attribute of the anonymousAuthentication + node in the IIS Manager GUI: + + Open the IIS Manager GUI and navigate to the desired server, site, or application + In Features View, find and double-click the Authentication icon + Select the Anonymous Authentication option and in the Actions pane select Edit... + Choose Application pool identity in the modal window and then press the OK button + + OR + +To use AppCmd.exe to configure anonymousAuthentication + at the server level, the command would look like this: + %systemroot%\system32\inetsrv\appcmd set config -section:anonymousAuthentication /username:"" --password + + OR + Enter the following command in PowerShell to configure: + Set-ItemProperty -Path IIS:\AppPools\<apppool name> -Name passAnonymousToken -Value True + + Impact: + + N/A + + + + + + + + + + + + + (L1) Ensure' WebDav' feature is disabled + + WebDAV is an extension to the HTTP protocol which allows clients to create, move, and delete files and resources on the web server. + + Note: + The WebDAV feature must be enabled for this functionality to be available in IIS. + + + + + + + Devices + Protect + + + + Applications + Protect + + + + + + Devices + Protect + + + + + + WebDAV is not widely used, and it has serious security concerns because it may allow clients to modify unauthorized files on the web server. Therefore, the WebDav feature should be disabled. + + + + + + + + To disable this feature using PowerShell, enter the following command: + Uninstall-WindowsFeature Web-DAV-Publishing + + Verify that Success is True + Impact: + + The WebDav feature will not be available in IIS. + + + + + + + + Configure Authentication and Authorization + + This section contains recommendations around the different layers of authentication in IIS. + + + (L1) Ensure 'global authorization rule' is set to restrict access + + IIS introduced URL Authorization, which allows the addition of Authorization rules to the actual URL, instead of the underlying file system resource, as a way to protect it. Authorization rules can be configured at the server, web site, folder (including Virtual Directories), or file level. The native URL Authorization module applies to all requests, whether they are .NET managed or other types of files (e.g., static files or ASP files). It is recommended that URL Authorization be configured to only grant access to the necessary security principals. + + + + + + + Data + Protect + + + + + + + + + Configuring a global Authorization rule that restricts access will ensure inheritance of the settings down through the hierarchy of web directories; if that content is copied elsewhere, the authorization rules flow with it. This will ensure access to current and future content is only granted to the appropriate principals, mitigating risk of accidental or unauthorized access. + + + + http://www.iis.net/learn/manage/configuring-security/understanding-iis-url-authorization + http://www.iis.net/learn/get-started/whats-new-in-iis-7/changes-in-security-between-iis-60-and-iis-7-and-above#Authorization + + + + To configure URL Authorization at the server level using command line utilities: + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config -section:system.webServer/security/authorization /-"[users='*',roles='',verbs='']" + + %systemroot%\system32\inetsrv\appcmd set config -section:system.webServer/security/authorization /+"[accessType='Allow',roles='Administrators']" + + OR + Enter the following command in PowerShell to configure: + Remove-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/authorization" -name "." -AtElement @{users='*';roles='';verbs=''} + + Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/authorization" -name "." -value @{accessType='Allow';roles='Administrators'} + + OR + To configure URL Authorization at the server level using IIS Manager: + + Connect to Internet Information Services (IIS Manager) + Select the server + Select Authorization Rules + Remove the "Allow All Users" rule + Click Add Allow Rule… + Allow access to the user(s), user groups, or roles that are authorized across all of the web sites and applications (e.g. the Administrators group) + + Impact: + + If not set properly, the authorization rule could restrict assess at a level that is not intended to be restricted. + + + + + + + (L1) Ensure access to sensitive site features is restricted to authenticated principals only + + IIS supports both challenge-based and login redirection-based authentication methods. Challenge-based authentication methods, such as Integrated Windows Authentication, require a client to respond correctly to a server-initiated challenge. A login redirection-based authentication method such as Forms Authentication relies on redirection to a login page to determine the identity of the principal. Challenge-based authentication and login redirection-based authentication methods cannot be used in conjunction with one another. + Public servers/sites are typically configured to use Anonymous Authentication. This method typically works, provided the content or services is intended for use by the public. When sites, applications, or specific content containers are not intended for anonymous public use, an appropriate authentication mechanism should be utilized. Authentication will help confirm the identity of clients who request access to sites, application, and content. IIS provides the following authentication modules by default: + + Anonymous Authentication - allows anonymous users to access sites, applications, and/or content + Integrated Windows Authentication - authenticates users using the NTLM or Kerberos protocols; Kerberos v5 requires a connection to Active Directory + ASP.NET Impersonation - allows ASP.NET applications to run under a security context different from the default security context for an application + Forms Authentication - enables a user to login to the configured space with a valid username and password which is then validated against a database or other credentials store + Basic authentication - requires a valid username and password to access content + Client Certificate Mapping Authentication - allows automatic authentication of users who log on with client certificates that have been configured; requires SSL + Digest Authentication - uses Windows domain controller to authenticate users who request access + + Note that none of the challenge-based authentication modules can be used at the same time Forms Authentication is enabled for certain applications/content. Forms Authentication does not rely on IIS authentication, so anonymous access for the ASP.NET application can be configured if Forms Authentication will be used. + It is recommended that sites containing sensitive information, confidential data, or non-public web services be configured with a credentials-based authentication mechanism. + + + + + + + Data + Protect + + + + + + Configuring authentication will help mitigate the risk of unauthorized users accessing data and/or services, and in some cases reduce the potential harm that can be done to a system. + + + http://learn.iis.net/page.aspx/377/using-aspnet-forms-authentication/rev/1 + http://learn.iis.net/page.aspx/244/how-to-take-advantage-of-the-iis7-integrated-pipeline/ + http://technet.microsoft.com/en-us/library/cc733010%28WS.10%29.aspx + http://msdn.microsoft.com/en-us/library/aa480476.aspx + https://technet.microsoft.com/en-us/library/hh831496(v=ws.11).aspx + + + + When configuring an authentication module for the first time, each mechanism must be completely configured before use. + +Enabling authentication can be performed by using the user interface (UI), running AppCmd.exe + commands in a command-line window, editing configuration files directly, or by writing WMI scripts. To verify an authentication mechanism is in place for sensitive content using the IIS Manager GUI: + + Open IIS Manager and navigate to level with sensitive content + In Features View, double-click Authentication + On the Authentication page, make sure an authentication module is enabled, while anonymous authentication is enabled (Forms Authentication can have anonymous as well) + If necessary, select the desired authentication module, then in the Actions pane, click Enable + + OR + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config -section:system.web/authentication /mode:<Windows|Passport|Forms> + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -location '<website location>' -filter 'system.webServer/security/authentication/anonymousAuthentication' -name 'enabled' -value 'False' + + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -location '<website location>' -filter 'system.webServer/security/authentication/windowsAuthentication' -name 'enabled' -value 'True' + + Impact: + + Authentication will be restricted to the method that is applied. + + + + + + + (L1) Ensure 'forms authentication' require SSL + + Forms-based authentication can pass credentials across the network in clear text. It is therefore imperative that the traffic between client and server be encrypted using SSL, especially in cases where the site is publicly accessible. It is recommended that communications with any portion of a site using Forms Authentication be encrypted using SSL. + + NOTE + Due to identified security vulnerabilities, SSL no longer provides adequate protection for a sensitive information. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Requiring SSL for Forms Authentication will protect the confidentiality of credentials during the login process, helping mitigate the risk of stolen user information. + + + + http://technet.microsoft.com/en-us/library/cc771077(WS.10).aspx + + + + + Open IIS Manager and navigate to the appropriate tier + In Features View, double-click Authentication + On the Authentication page, select Forms Authentication + In the Actions pane, click Edit + Check the Requires SSL checkbox in the cookie settings section, click OK + + OR + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config -section:system.web/authentication /mode:Forms + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/Default Web Site' -filter 'system.web/authentication/forms' -name 'requireSSL' -value 'True' + + Impact: + + None. + + + + + + + + + + + + + (L2) Ensure 'forms authentication' is set to use cookies + + Forms Authentication can be configured to maintain the site visitor's session identifier in either a URI or cookie. It is recommended that Forms Authentication be set to use cookies. + + + + + + + + + + +Using cookies to manage session state may help mitigate the risk of session hi-jacking attempts by preventing ASP.NET from having to move session information to the URL. Moving session information identifiers into the URL may cause session IDs to show up in proxy logs, browsing history, and be accessible to client scripting via document.location +. + + + http://technet.microsoft.com/en-us/library/cc732830%28WS.10%29.aspx + + + + + Open IIS Manager and navigate to the level where Forms Authentication is enabled + In Features View, double-click Authentication + On the Authentication page, select Forms Authentication + In the Actions pane, click Edit + In the Cookie settings section, select Use cookies from the Mode dropdown + + OR + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config -section:system.web/authentication /forms.cookieless:"UseCookies" + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/Default Web Site' -filter 'system.web/authentication/forms' -name 'cookieless' -value 'UseCookies' + + Impact: + + Site visitor's session identifier will be stored via cookies. + + + + + + + + + + + + + (L1) Ensure 'cookie protection mode' is configured for forms authentication + + The cookie protection mode defines the protection Forms Authentication cookies will be given within a configured application. The four cookie protection modes that can be defined are: + + Encryption and validation - Specifies that the application use both data validation and encryption to help protect the cookie; this option uses the configured data validation algorithm (based on the machine key) and triple-DES (3DES) for encryption, if available and if the key is long enough (48 bytes or more) + None - Specifies that both encryption and validation are disabled for sites that are using cookies only for personalization and have weaker security requirements + Encryption - Specifies that the cookie is encrypted by using Triple-DES or DES, but data validation is not performed on the cookie; cookies used in this manner might be subject to plain text attacks + Validation - Specifies that a validation scheme verifies that the contents of an encrypted cookie have not been changed in transit + + It is recommended that cookie protection mode always encrypt and validate Forms Authentication cookies. + + + + + + + Data + Protect + + + + + + + + + By encrypting and validating the cookie, the confidentiality and integrity of data within the cookie is assured. This helps mitigate the risk of attacks such as session hijacking and impersonation. + + + + http://technet.microsoft.com/en-us/library/cc731804%28WS.10%29.aspx + + + + +Cookie protection mode can be configured by using the user interface (UI), by running Appcmd.exe + commands in a command-line window, by editing configuration files directly, or by writing WMI scripts. Using IIS Manager: + + Open IIS Manager and navigate to the level where Forms Authentication is enabled + In Features View, double-click Authentication + On the Authentication page, select Forms Authentication + In the Actions pane, click Edit + In the Cookie settings section, verify the drop-down for Protection mode is set for Encryption and validation + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/<website name>' -filter 'system.web/authentication/forms' -name 'protection' -value 'All' + + Impact: + + Protection Forms Authentication cookies will restricted to the mode defined. + + + + + + + + + + + + + (L1) Ensure transport layer security for 'basic authentication' is configured + + Basic Authentication can pass credentials across the network in clear text. It is therefore imperative that the traffic between client and server be encrypted, especially in cases where the site is publicly accessible and is recommended that TLS be configured and required for any Site or Application using Basic Authentication. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Credentials sent in clear text can be easily intercepted by malicious code or persons. Enforcing the use of Transport Layer Security will help mitigate the chances of hijacked credentials. + + + + http://technet.microsoft.com/en-us/library/dd378853%28WS.10%29.aspx + + + + To protect Basic Authentication with transport layer security: + + Open IIS Manager + In the Connections pane on the left, select the server to be configured + In the Connections pane, expand the server, then expand Sites and select the site to be configured + In the Actions pane, click Bindings; the Site Bindings dialog appears + If an HTTPS binding is available, click Close and see below "To require SSL" + If no HTTPS binding is visible, perform the following steps + + To add an HTTPS binding: + + In the Site Bindings dialog, click Add; the Add Site Binding dialog appears + Under Type, select https + Under SSL certificate, select an X.509 certificate + Click OK, then close + + To require SSL: + + In Features View, double-click SSL Settings + On the SSL Settings page, select Require SSL. + In the Actions pane, click Apply + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -location '<website name>' -filter 'system.webServer/security/access' -name 'sslFlags' -value 'Ssl' + + Impact: + + Credentials will not be passed across the network in plain text. + + + + + + + (L1) Ensure 'passwordFormat' is not set to clear + + +The <credentials> + element of the <authentication> + element allows optional definitions of name and password for IIS Manager User accounts within the configuration file. Forms based authentication also uses these elements to define the users. IIS Manager Users can use the administration interface to connect to sites and applications in which they've been granted authorization. + + Note: + The <credentials> + element only applies when the default provider, ConfigurationAuthenticationProvider +, is configured as the authentication provider. + +It is recommended that passwordFormat + be set to a value other than Clear +, such as SHA1 +. + + + + + + + Data + Protect + + + + + + Users + Protect + + + + + + Authentication credentials should always be protected to reduce the risk of stolen authentication credentials. + + + + http://www.iis.net/ConfigReference/system.webServer/management/authentication/credentials + http://msdn.microsoft.com/en-us/library/bb422401%28VS.90%29.aspx + https://docs.microsoft.com/en-us/dotnet/framework/whats-new/#v471 + + + + +Authentication mode is configurable at the machine.config +, root-level web.config +, or application-level web.config +: + + Locate and open the configuration file where the credentials are stored + +Find the <credentials> + element + +If present, ensure passwordFormat + is not set to Clear + + +Change passwordFormat +to SHA1 + + + The clear text passwords will need to be replaced with the appropriate hashed version. + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/<website name>' -filter 'system.web/authentication/forms/credentials' -name 'passwordFormat' -value 'SHA1' + + Impact: + + + passwordFormat + will be encrypted. + + + + + + + + + + + + + (L2) Ensure 'credentials' are not stored in configuration files + + +The <credentials> + element of the <authentication> + element allows optional definitions of name and password for IIS Manager User accounts within the configuration file. Forms based authentication also uses these elements to define the users. IIS Manager Users can use the administration interface to connect to sites and applications in which they've been granted authorization. + + Note: + The <credentials> + element only applies when the default provider, ConfigurationAuthenticationProvider +, is configured as the authentication provider. + It is recommended to avoid storing passwords in the configuration file even in form of hash. + + + + + + + Users + Protect + + + + + + Authentication credentials should always be protected to reduce the risk of stolen authentication credentials. For security reasons, it is recommended that user credentials not be stored an any IIS configuration files. + + + http://www.iis.net/ConfigReference/system.webServer/management/authentication/credentials + http://msdn.microsoft.com/en-us/library/bb422401%28VS.90%29.aspx + + + + +Authentication mode is configurable at the machine.config +, root-level web.config +, or application-level web.config +: + + Locate and open the configuration file where the credentials are stored + +Find the <credentials> + element + If present, remove the section + + This will remove all references to stored users in the configuration files. + OR + Enter the following command in PowerShell to configure: + Remove-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/<website name>' -filter 'system.web/authentication/forms/credentials' -name '.' + + Impact: + + Passwords in the configuration file will be stored in form of a hash. + + + + + + + + + + + + + + ASP.NET Configuration Recommendations + + This section contains recommendations specific to ASP.NET. + + + (L1) Ensure 'deployment method retail' is set + + +The <deployment retail> + switch is intended for use by production IIS servers. This switch is used to help applications run with the best possible performance and least possible security information leakages by disabling the application's ability to generate trace output on a page, disabling the ability to display detailed error messages to end users, and disabling the debug switch. Often times, switches and options that are developer-focused, such as failed request tracing and debugging, are enabled during active development. + +It is recommended that the deployment method on any production server be set to retail +. + + + + + + + + + + +Utilizing the switch specifically intended for production IIS servers will eliminate the risk of vital application and system information leakages that would otherwise occur if tracing or debug were to be left enabled, or customErrors + were to be left off. + + + http://msdn.microsoft.com/en-US/library/ms228298%28VS.80%29.aspx + + + + + +Open the machine.config + file located in: %systemroot%\Microsoft.NET\Framework<bitness (if not the 32 bit)>\<framework version>\CONFIG + + +Add the line <deployment retail="true" /> + within the <system.web> + section + +If systems are 64-bit, do the same for the machine.config + located in: %systemroot%\Microsoft.NET\Framework<bitness (if not the 32 bit)>\<framework version>\CONFIG + + + Impact: + + N/A + + + + + + + (L2) Ensure 'debug' is turned off + + Developers often enable the debug mode during active ASP.NET development so that they do not have to continually clear their browsers cache every time they make a change to a resource handler. The problem would arise from this being left "on" or set to "true". Compilation debug output is displayed to the end user, allowing malicious persons to obtain detailed information about applications. + +This is a defense in depth recommendation due to the <deployment retail="true" /> + in the machine.config + configuration file overriding any debug settings. + It is recommended that debugging still be turned off. + + + + + + + + + + +Setting <compilation debug> + to false + ensures that detailed error information does not inadvertently display during live application usage, mitigating the risk of application information leakage falling into unscrupulous hands. + + + http://technet.microsoft.com/en-us/library/cc725812%28WS.10%29.aspx + + + + To use the UI to make this change: + + Open IIS Manager and navigate desired server, site, or application + In Features View, double-click .NET Compilation + On the .NET Compilation page, in the Behavior section, ensure the Debug field is set to False + When finished, click Apply in the Actions pane + + +Note: The <compilation debug> + switch will not be present in the web.config + file unless it has been added manually, or has previously been configured using the IIS Manager GUI. + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/<website name>' -filter "system.web/compilation" -name "debug" -value "False" + + Impact: + + Debugging will be disabled. + + + + + + + + + + + + + (L2) Ensure custom error messages are not off + + +When an ASP.NET application fails and causes an HTTP/1.x 500 Internal Server Error, or a feature configuration (such as Request Filtering) prevents a page from being displayed, an error message will be generated. Administrators can choose whether or not the application should display a friendly message to the client, detailed error message to the client, or detailed error message to localhost only. The <customErrors> + tag in the web.config + has three modes: + + +On: Specifies that custom errors are enabled. If no defaultRedirect + attribute is specified, users see a generic error. The custom errors are shown to the remote clients and to the local host + Off: Specifies that custom errors are disabled. The detailed ASP.NET errors are shown to the remote clients and to the local host + RemoteOnly: Specifies that custom errors are shown only to the remote clients, and that ASP.NET errors are shown to the local host. This is the default value + + +This is a defense in depth recommendation due to the <deployment retail="true" /> + in the machine.config + file overriding any settings for customErrors + to be turned Off +. + +It is recommended that customErrors + still be turned to On +or RemoteOnly +. + + + + + + + + + + + customErrors + can be set to On +or RemoteOnly + without leaking detailed application information to the client. Ensuring that customErrors + is not set to Off +will help mitigate the risk of malicious persons learning detailed application error and server configuration information. + + + http://technet.microsoft.com/en-us/library/dd569096%28WS.10%29.aspx + + + + + customErrors + may be set for a server, site, or application using the IIS Manager GUI, using AppCmd.exe + commands in a command-line window, directly editing the configuration files, or by writing WMI scripts. Perform the following to set the customErrors + mode to RemoteOnly + or On + for a Web Site in the IIS Manager GUI: + + Open the IIS Manager GUI and navigate to the site to be configured + In Features View, find and double-click .NET Error Pages icon + In the Actions Pane, click Edit Feature Settings + In modal dialog, choose On or Remote Only for Mode settings + Click OK + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/Default Web Site' -filter "system.web/customErrors" -name "mode" -value "RemoteOnly" + + Impact: + + N/A + + + + + + + + + + + + + (L1) Ensure IIS HTTP detailed errors are hidden from displaying remotely + + +A Web site's error pages are often set to show detailed error information for troubleshooting purposes during testing or initial deployment. To prevent unauthorized users from viewing this privileged information, detailed error pages must not be seen by remote users. This setting can be modified in the errorMode + attribute setting for a Web site's error pages. By default, the errorMode + attribute is set in the Web.config + file for the Web site or application and is located in the <httpErrors> + element of the <system.webServer> + section. + It is recommended that custom errors be prevented from displaying remotely. + + + + + + + + + + The information contained in custom error messages can provide clues as to how applications function, opening up unnecessary attack vectors. Ensuring custom errors are never displayed remotely can help mitigate the risk of malicious persons obtaining information as to how the application works. + + + http://technet.microsoft.com/en-us/library/dd391900%28WS.10%29.aspx + http://www.iis.net/configreference/system.webserver/httperrors + + + + +The following describes how to change the errorMode + attribute to DetailedLocalOnly + or Custom + for a Web site by using IIS Manager: + + Open IIS Manager with Administrative privileges + In the Connections pane on the left, expand the server, then expand the Sites folder + Select the Web site or application to be configured + In Features View, select Error Pages, in the Actions pane, select Open Feature + In the Actions pane, select Edit Feature Settings + In the Edit Error Pages Settings dialog, under Error Responses, select either Custom error pages or Detailed errors for local requests and custom error pages for remote requests + Click OK and exit the Edit Error Pages Settings dialog + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/<website name>' -filter "system.webServer/httpErrors" -name "errorMode" -value "DetailedLocalOnly" + + Impact: + + Custom errors will not be viewable remotely. + + + + + + + + + + + + + (L2) Ensure ASP.NET stack tracing is not enabled + + +The trace + element configures the ASP.NET code tracing service that controls how trace results are gathered, stored, and displayed. When tracing is enabled, each page request generates trace messages that can be appended to the page output or stored in an application trace log. + +This is a defense in depth recommendation due to the <deployment retail="true" /> + in the machine.config + file overriding any settings for ASP.NET stack tracing that are left on. + It is recommended that ASP.NET stack tracing still be turned off. + + + + + + + + + + In an active Web Site, tracing should not be enabled because it can display sensitive configuration and detailed stack trace information to anyone who views the pages in the site. + +If necessary, the localOnly + attribute can be set to true to have trace information displayed only for localhost requests. Ensuring that ASP.NET stack tracing is not on will help mitigate the risk of malicious persons learning detailed stack trace information. + + + http://msdn.microsoft.com/en-us/library/94c55d08%28v=vs.100%29.aspx + http://msdn.microsoft.com/en-us/library/0x5wc973%28v=vs.100%29.aspx + + + + + +Ensure <deployment retail="true" /> +is enabled in the machine.config +. + Remove all attribute references to ASP.NET tracing by deleting the trace and trace enable attributes. + + Per Page: + Remove any references to: + Trace="true" + + Per Application: + +<configuration>
+ <system.web>
+ <trace enabled="true">
+ </system.web>
+</configuration> +
+ OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/<website name>' -filter "system.web/trace" -name "enabled" -value "False" + + Impact: + + ASP.NET stack tracing still be turned off and sensitive configuration and detailed stack trace information will not be viewable to anyone who views the pages in the site. + +
+
+
+ + + + + + +
+ + (L2) Ensure 'httpcookie' mode is configured for session state + + A session cookie associates session information with client information for that session, which can be the duration of a user's connection to a site. The cookie is passed in a HTTP header together with all requests between the client and server. + +Session information can also be stored in the URL. However, storing session information in this manner has security implications that can open attack vectors such as session hijacking. An effective method used to prevent session hijacking attacks is to force web applications to use cookies to store the session token. This is accomplished by setting the cookieless + attribute of the sessionState + node to UseCookies + or False + which will in turn keep session state data out of URI. + +It is recommended that session state be configured to UseCookies +. + + + + + + + + + + +Cookies that have been properly configured help mitigate the risk of attacks such as session hi-jacking attempts by preventing ASP.NET from having to move session information to the URL; moving session information in URI causes session IDs to show up in proxy logs and is accessible to client scripting via document.location +. + + + http://www.iis.net/learn/application-frameworks/scenario-build-an-aspnet-website-on-iis/planning-step-2-plan-asp-net-settings + http://msdn.microsoft.com/en-us/library/h6bb9cz9%28VS.71%29.aspx + + + + + SessionState + can be set to UseCookies + by using the IIS Manager GUI, using AppCmd.exe + commands in a command-line window, directly editing the configuration files, or by writing WMI scripts. Perform the following to set the cookieless + attribute of the sessionState + node to UseCookies + in the IIS Manager GUI: + + Open the IIS Manager GUI and navigate desired server, site, or application + In Features View, find and double-click the Session State icon + In the Cookie Settings section, choose Use Cookies from the Mode dropdown + In the Actions Pane, click Apply + + +To use AppCmd.exe + to configure sessionState + at the server level, the command would look like this: + %systemroot%\system32\inetsrv\appcmd set config /commit:WEBROOT /section:sessionState /cookieless:UseCookies /cookieName:ASP.NET_SessionID /timeout:20 + + +When Appcmd.exe + is used to configure the <sessionstate> + element at the global level in IIS, the /commit:WEBROOT + switch must be included so that configuration changes are made to the root web.config + file instead of ApplicationHost.config +. + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/<website name>' -filter "system.web/sessionState" -name "mode" -value "StateServer" + + Impact: + + Session information in URI session IDs will not show up in proxy logs. + + + + + + + + + + + + + (L1) Ensure 'cookies' are set with HttpOnly attribute + + +The httpOnlyCookies + attribute of the httpCookies + node determines if IIS will set the HttpOnly + flag on HTTP cookies it sets. The HttpOnly + flag indicates to the user agent that the cookie must not be accessible by client-side script (i.e document.cookie). + +It is recommended that the httpOnlyCookies + attribute be set to true +. + + + + + + + + + + +When cookies are set with the HttpOnly + flag, they cannot be accessed by client-side scripting running in the user's browser. Preventing client-side scripting from accessing cookie content may reduce the probability of a cross site scripting attack materializing into a successful session hijack. + + + https://tools.ietf.org/wg/httpstate/charters + https://www.owasp.org/index.php/HTTPOnly#Browsers_Supporting_HttpOnly + https://msdn.microsoft.com/en-us/library/ms533046.aspx + + + + + +Locate and open the application's web.config + file + +Add the <httpCookies httpOnlyCookies="true" /> + tag within <system.web> +: + + +<configuration>
+ <system.web>
+ <httpCookies httpOnlyCookies="true" />
+ </system.web>
+</configuration> +
+ +Setting the value of the httpOnlyCookies + attribute of the httpCookies + element to true + will add the HttpOnly + flag to all the cookies set by the application. All modern versions of browsers recognize HttpOnly + attribute; older versions will either treat them as normal cookies or simply ignore them altogether. + Impact: + + N/A + +
+
+
+ + + + + + +
+ + (L2) Ensure 'MachineKey validation method - .Net 3.5' is configured + + +The machineKey + element of the ASP.NET web.config + specifies the algorithm and keys that ASP.NET will use for encryption. The Machine Key feature can be managed to specify hashing and encryption settings for application services such as view state, Forms authentication, membership and roles, and anonymous identification. + The following validation methods are available: + + Advanced Encryption Standard (AES) is relatively easy to implement and requires little memory. AES has a key size of 128, 192, or 256 bits. This method uses the same private key to encrypt and decrypt data, whereas a public-key method must use a pair of keys + Message Digest 5 (MD5) is used for digital signing of applications. This method produces a 128-bit message digest, which is a compressed form of the original data + Secure Hash Algorithm (SHA1) is considered more secure than MD5 because it produces a 160-bit message digest + Triple Data Encryption Standard (TripleDES) is a minor variation of Data Encryption Standard (DES). It is three times slower than regular DES but can be more secure because it has a key size of 192 bits. If performance is not a primary consideration, consider using TripleDES + + It is recommended that AES or SHA1 methods be configured for use at the global level. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Setting the validation property to AES will provide confidentiality and integrity protection to the viewstate. AES is the strongest encryption algorithm supported by the validation property. Setting the validation property to SHA1 will provide integrity protection to the viewstate. SHA1 is the strongest hashing algorithm supported by the validation property. + + + + http://technet.microsoft.com/en-us/library/cc772271%28WS.10%29.aspx + http://technet.microsoft.com/en-us/library/cc772287%28WS.10%29.aspx + + + + +Machine key encryption can be set by using the UI, running appcmd.exe + commands, by editing configuration files directly, or by writing WMI scripts. To set the Machine Key encryption at the global level using an appcmd.exe + command: + %systemroot%\system32\inetsrv\appcmd set config /commit:WEBROOT /section:machineKey /validation:SHA1 + + +Note: When Appcmd.exe + is used to configure the <machineKey> + element at the global level in IIS, the /commit:WEBROOT + switch must be included so that configuration changes are made to the root web.config + file instead of ApplicationHost.config +. + Impact: + + N/A + + + + + + + + + + + + + (L1) Ensure 'MachineKey validation method - .Net 4.5' is configured + + +The machineKey + element of the ASP.NET web.config + specifies the algorithm and keys that ASP.NET will use for encryption. The Machine Key feature can be managed to specify hashing and encryption settings for application services such as view state, Forms authentication, membership and roles, and anonymous identification. + The following validation methods are available: + + Advanced Encryption Standard (AES) is relatively easy to implement and requires little memory. AES has a key size of 128, 192, or 256 bits. This method uses the same private key to encrypt and decrypt data, whereas a public-key method must use a pair of keys + Message Digest 5 (MD5) is used for digital signing of applications. This method produces a 128-bit message digest, which is a compressed form of the original data + Secure Hash Algorithm (SHA1) is considered more secure than MD5 because it produces a 160-bit message digest + Triple Data Encryption Standard (TripleDES) is a minor variation of Data Encryption Standard (DES). It is three times slower than regular DES but can be more secure because it has a key size of 192 bits. If performance is not a primary consideration, consider using TripleDES + Secure Hash Algorithm (SHA-2) is a family of two similar hash functions, with different block sizes known as SHA-256 and SHA-512. They differ in the word size; SHAS-256 used 32-bit words and SHA-512 uses 64-bit words. + + It is recommended that SHA-2 methods be configured for use at the global level. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + SHA-2 is the strongest hashing algorithm supported by the validation property so it should be used as the validation method for the MachineKey in .Net 4.5. + + + + http://www.iis.net/learn/get-started/whats-new-in-iis-8/iis-80-aspnet-configuration-management + + + + +Machine key encryption can be set by using the UI, running appcmd.exe + commands, by editing configuration files directly, or by writing WMI scripts. To set the Machine Key encryption at the global level using an appcmd.exe + command: + %systemroot%\system32\inetsrv\appcmd set config /commit:WEBROOT /section:machineKey /validation:<validation method> + + +Note: When Appcmd.exe + is used to configure the <machineKey> + element at the global level in IIS, the /commit:WEBROOT + switch must be included so that configuration changes are made to the root web.config + file instead of ApplicationHost.config +. + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT' -filter "system.web/machineKey" -name "validation" -value "<validation method>" + + Impact: + + N/A + + + + + + + + + + + + + (L1) Ensure global .NET trust level is configured + + An application's trust level determines the permissions that are granted by the ASP.NET code access security (CAS) policy. CAS defines two trust categories: full trust and partial trust. An application that has full trust permissions may access all resource types on a server and perform privileged operations, while applications that run with partial trust have varying levels of operating permissions and access to resources. + The possible values for the Level property of the TrustSection class are: + + Full: Specifies unrestricted permissions and grants the ASP.NET application permissions to access any resource that is subject to operating system security; all privileged operations are supported + +High: specifies a high level of code access security which limits the application from doing the following: + + Call unmanaged code + Call serviced components + Write to the event log + Access Microsoft Windows Message Queuing queues + Access ODBC, OLD DB, or Oracle data sources + + + +Medium: specifies a medium level of code access security, which means that in addition to the restrictions for High, the ASP.NET application cannot do any of the following things: + + Access files outside the application directory + Access the registry + + + +Low: specifies a low level of code access security, which means that in addition to the restrictions for Medium, the application is prevented from performing any of the following actions: + + Write to the file system + +Call the System.Security.CodeAccessPermission.Assert + method to expand permissions to resources + Minimal: specifies a minimal level of code access security, which means that the application has only execute permission + + + + It is recommended that the global .NET Trust Level be set to Medium or lower. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + The CAS determines the permissions that are granted to the application on the server. Setting a minimal level of trust that is compatible with the applications will limit the potential harm that a compromised application could cause to a system. + + + + http://technet.microsoft.com/en-us/library/cc772237(WS.10).aspx + http://msdn.microsoft.com/en-us/library/ms691448%28VS.90%29.aspx + http://support.microsoft.com/kb/2698981 + + + + +Trust level can be set by using the UI, running appcmd.exe + commands, by editing configuration files directly, or by writing WMI scripts. To set the .Net Trust Level to Medium at the server level using an appcmd.exe + command: + %systemroot%\system32\inetsrv\appcmd set config /commit:WEBROOT /section:trust /level:Medium + + +When Appcmd.exe + is used to configure the element at the global level in IIS, the /commit:WEBROOT + switch must be included so that configuration changes are made to the root web.config + file instead of ApplicationHost.config +. + + OR + + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT' -filter "system.web/trust" -name "level" -value "Medium" + + Impact: + + If not set properly, the application may not run. + + + + + + + + + + + + + (L2) Ensure X-Powered-By Header is removed + + The x-powered-by headers specify the underlying technology used by the webserver. + + + + + + + Devices + Protect + + + + + + Attackers are able to conduct reconnaissance on a website using these response headers. This header could be used to target attacks for specific known vulnerabilities associated with the underlying technology. Removing this header will prevent targeting of your application for specific exploits by non-determined attackers. + While this is not the only way to fingerprint a site through the response headers, it makes it harder and prevents some potential attackers. + + + https://blogs.msdn.microsoft.com/jpsanders/2015/10/07/remove-server-and-x-powered-by-headers-from-your-azure-mobile-apps/ + + + + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd.exe set config -section:system.webServer/httpProtocol /-"customHeaders.[name='X-Powered-By']" /commit:apphost + + OR + Enter the following command in PowerShell to configure: + Remove-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webserver/httpProtocol/customHeaders" -name "." -AtElement @{name='X-Powered-By'} + + Impact: + + X-powered-by headers will not be available on the webserver. + + + + + + + (L2) Ensure Server Header is removed + + The server header headers specify the underlying technology used by the application. + + + + + + + Devices + Protect + + + + + + While this is not the only way to fingerprint a site through the response headers, it makes it harder and prevents some potential attackers. The server header removal directive is a new feature in IIS 10 that can assist in mitigating this risk. + + + https://blogs.msdn.microsoft.com/jpsanders/2015/10/07/remove-server-and-x-powered-by-headers-from-your-azure-mobile-apps/ + + + + Enter the following command to use AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd.exe set config -section:system.webServer/security/requestFiltering /removeServerHeader:"True" /commit:apphost + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST/' -filter "system.webServer/security/requestFiltering" -name "removeServerHeader" -value "True" + + Impact: + + This will remove the server header. + + + + + +
+ + Request Filtering and Other Restriction Modules + + Introduced in IIS 7.0 for the first time, Request Filtering is a powerful module that provides a configurable set of rules that enables administrators to allow or reject the types of requests that they determine should be allowed or rejected at the server, web site, or web application levels. + Earlier versions of Internet Information Services provided the tool UrlScan, which was provided as an add-on to enable system administrators to enforce tighter security policies on their web servers. All of the core features of URLScan have been incorporated into the Request Filtering module. Due to the close nature of functionality in these two tools, reference to legacy URLScan settings will be made where applicable. + IIS 8 also introduced modules for Dynamic IP Address Restrictions. This module can be configured to automatically block web site access based on specific rules. + Note: Request Filtering and IP and Domain Restrictions must be enabled as a role service under IIS in order to configure any of its features. + + + (L2) Ensure 'maxAllowedContentLength' is configured + + +The maxAllowedContentLength + Request Filter is the maximum size of the http request, measured in bytes, which can be sent from a client to the server. Configuring this value enables the total request size to be restricted to a configured value. + It is recommended that the overall size of requests be restricted to a maximum value appropriate for the server, site, or application. + + + + + + + + + + +Setting an appropriate value that has been tested for the maxAllowedContentLength + filter will lower the impact an abnormally large request would otherwise have on IIS and/or web applications. This helps to ensure availability of web content and services, and may also help mitigate the risk of buffer overflow type attacks in unmanaged components. + + + http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits + http://learn.iis.net/page.aspx/143/use-request-filtering/ + + + + +The MaxAllowedContentLength + Request Filter may be set for a server, website, or application using the IIS Manager GUI, using AppCmd.exe + commands in a command-line window, and/or directly editing the configuration files. To configure using the IIS Manager GUI: + + Open Internet Information Services (IIS) Manager + In the Connections pane, click on the server, site, application, or directory to be configured + In the Home pane, double-click Request Filtering + Click Edit Feature Settings... in the Actions pane + Under the Request Limits section, key the maximum content length in bytes that will allow applications to retain their intended functionality, such as 30000000 (approx. 28.6 MB) + + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config /section:requestfiltering /requestLimits.maxAllowedContentLength:30000000 + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/requestFiltering/requestLimits" -name "maxAllowedContentLength" -value 30000000 + + Impact: + + Size of requests be restricted to the maximum value set. + + + + + + + (L2) Ensure 'maxURL request filter' is configured + + +The maxURL + attribute of the <requestLimits> + property is the maximum length (in Bytes) in which a requested URL can be (excluding query string) in order for IIS to accept. Configuring this Request Filter enables administrators to restrict the length of the requests that the server will accept. + It is recommended that a limit be put on the length of URL. + + + + + + + + + + With a properly configured Request Filter limiting the amount of data accepted in the URL, chances of undesired application behaviors affecting the availability of content and services are reduced. + + + http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits + http://learn.iis.net/page.aspx/143/use-request-filtering/ + + + + +The MaxURL + Request Filter may be set for a server, website, or application using the IIS Manager GUI, using AppCmd.exe + commands in a command-line window, and/or directly editing the configuration files. To configure using the IIS Manager GUI: + + Open Internet Information Services (IIS) Manager + In the Connections pane, click on the connection, site, application, or directory to be configured + In the Home pane, double-click Request Filtering + Click Edit Feature Settings... in the Actions pane + Under the Request Limits section, key the maximum URL length in bytes that has been tested with web applications + + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config /section:requestfiltering /requestLimits.maxURL:4096 + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/requestFiltering/requestLimits" -name "maxUrl" -value 4096 + + Impact: + + Length of the URL will be restricted to the maximum value set. + + + + + + + + + + + + + (L2) Ensure 'MaxQueryString request filter' is configured + + +The MaxQueryString + Request Filter describes the upper limit on the length of the query string that the configured IIS server will allow for websites or applications. + It is recommended that values always be established to limit the amount of data that can be accepted in the query string. + + + + + + + + + + With a properly configured Request Filter limiting the amount of data accepted in the query string, chances of undesired application behaviors such as app pool failures are reduced. + + + http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits + http://learn.iis.net/page.aspx/143/use-request-filtering/ + + + + +The MaxQueryString + Request Filter may be set for a server, website, or application using the IIS Manager GUI, using AppCmd.exe + commands in a command-line window, and/or directly editing the configuration files. To configure using the IIS Manager GUI: + + Open Internet Information Services (IIS) Manager + In the Connections pane, go to the connection, site, application, or directory to be configured + In the Home pane, double-click Request Filtering + Click Edit Feature Settings... in the Actions pane + Under the Request Limits section, key in a safe upper bound in the Maximum query string (Bytes) textbox + + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config /section:requestfiltering /requestLimits.maxQueryString:2048 + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/requestFiltering/requestLimits" -name "maxQueryString" -value 2048 + + Impact: + + The amount of data to be accepted in the query string will be limited. + + + + + + + + + + + + + (L2) Ensure non-ASCII characters in URLs are not allowed + + +This feature is used to allow or reject all requests to IIS that contain non-ASCII characters. When using this feature, Request Filtering will deny the request if high-bit characters are present in the URL. The UrlScan equivalent is AllowHighBitCharacters +. + It is recommended that requests containing non-ASCII characters be rejected, where possible. + + + + + + + + + + This feature can help defend against canonicalization attacks, reducing the potential attack surface of servers, sites, and/or applications. + + + http://learn.iis.net/page.aspx/143/use-request-filtering/ + http://learn.iis.net/page.aspx/936/urlscan-1-reference/ + Professional IIS 7 by Ken Schaefer, Jeff Cochran, Scott Forsyth, Rob Baugh, Mike Everest, Dennis Glendenning + + + + +The AllowHighBitCharacters + Request Filter may be set for a server, website, or application using the IIS Manager GUI, using AppCmd.exe + commands in a command-line window, and/or directly editing the configuration files. To configure using the IIS Manager GUI: + + Open Internet Information Services (IIS) Manager + In the Connections pane, go to the connection, site, application, or directory to be configured + In the Home pane, double-click Request Filtering + Click Edit Feature Settings... in the Actions pane + Under the General section, uncheck Allow high-bit characters + + Note: Disallowing high-bit ASCII characters in the URL may negatively impact the functionality of sites requiring international language support. + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config /section:requestfiltering /allowHighBitCharacters:false + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/requestFiltering" -name "allowHighBitCharacters" -value "False" + + Impact: + + Requests containing non-ASCII characters be rejected. + + + + + + + + + + + + + (L1) Ensure Double-Encoded requests will be rejected + + +This Request Filter feature prevents attacks that rely on double-encoded requests and applies if an attacker submits a double-encoded request to IIS. When the double-encoded requests filter is enabled, IIS will go through a two iteration process of normalizing the request. If the first normalization differs from the second, the request is rejected and the error code is logged as a 404.11. The double-encoded requests filter was the VerifyNormalization + option in UrlScan. + It is recommended that double-encoded requests be rejected. + + + + + + + Devices + Protect + + + + + + + + + This feature will help prevent attacks that rely on URLs that have been crafted to contain double-encoded request(s). + + + + http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits + http://learn.iis.net/page.aspx/143/use-request-filtering/ + + + + +The allowDoubleEscaping + Request Filter may be set for a server, website, or application using the IIS Manager GUI, using AppCmd.exe + commands in a command-line window, and/or directly editing the configuration files. To configure using the IIS Manager GUI: + + Open Internet Information Services (IIS) Manager + In the Connections pane, select the site, application, or directory to be configured + In the Home pane, double-click Request Filtering + Click Edit Feature Settings... in the Actions pane + Under the General section, uncheck Allow double escaping + + +If a file name in a URL includes "+" then allowDoubleEscaping + must be set to true + to allow functionality. + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config /section:requestfiltering /allowDoubleEscaping:false + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/requestFiltering" -name "allowDoubleEscaping" -value "True" + + Impact: + + Double-encoded requests will be rejected. + + + + + + + + + + + + + (L1) Ensure 'HTTP Trace Method' is disabled + + +The HTTP TRACE method returns the contents of client HTTP requests in the entity-body of the TRACE response. Attackers could leverage this behavior to access sensitive information, such as authentication data or cookies, contained in the HTTP headers of the request. One such way to mitigate this is by using the <verbs> + element of the <requestFiltering> + collection. The <verbs> + element replaces the [AllowVerbs] and [DenyVerbs] features in UrlScan. + It is recommended the HTTP TRACE method be denied. + + + + + + + + + + Attackers may abuse HTTP TRACE functionality to gain access to information in HTTP headers such as cookies and authentication data. This risk can be mitigated by not allowing the TRACE verb. + + + http://www.kb.cert.org/vuls/id/867593 + http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/verbs + + + + + Open Internet Information Services (IIS) Manager + In the Connections pane, select the site, application, or directory to be configured + In the Home pane, double-click Request Filtering + In the Request Filtering pane, click the HTTP verbs tab, and then click Deny Verb... in the Actions pane + In the Deny Verb dialog box, enter the TRACE, and then click OK + + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config /section:requestfiltering /+verbs.[verb='TRACE',allowed='false'] + + OR + Enter the following command in PowerShell to configure: + Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/requestFiltering/verbs" -name "." -value @{verb='TRACE';allowed='False'} + + Impact: + + Contents of client HTTP requests in the entity-body of the TRACE response will not be available. + + + + + + + (L1) Ensure Unlisted File Extensions are not allowed + + +The FileExtensions + Request Filter allows administrators to define specific extensions their web server(s) will allow and disallow. The property allowUnlisted + will cover all other file extensions not explicitly allowed or denied. Often times, extensions such as .config +, .bat +, .exe +, to name a few, should never be served. The AllowExtensions + and DenyExtensions + options are the UrlScan equivalents. + It is recommended that all extensions be disallowed at the most global level possible, with only those necessary being allowed. + + + + + + + + + + Disallowing all but the necessary file extensions can greatly reduce the attack surface of applications and servers. + + + http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits + http://www.iis.net/learn/manage/configuring-security/configure-request-filtering-in-iis + + + + +The allowUnlisted + Request Filter may be set for a server, website, or application using the IIS Manager GUI, using AppCmd.exe + commands in a command-line window, and/or directly editing the configuration files. To configure at the server level using the IIS Manager GUI: + + Open Internet Information Services (IIS) Manager + In the Connections pane, select the server + In the Home pane, double-click Request Filtering + Click Edit Feature Settings... in the Actions pane + Under the General section, uncheck Allow unlisted file name extensions + + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config /section:requestfiltering /fileExtensions.allowunlisted:false + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/requestFiltering/fileExtensions" -name "allowUnlisted" -value "False" + + Impact: + + If not set properly, file extensions that are needed will be rejected. + + + + + + + + + + + + + (L1) Ensure Handler is not granted Write and Script/Execute + + +Handler mappings can be configured to give permissions to Read +, Write +, Script +, or Execute + depending on what the use is for - reading static content, uploading files, executing scripts, etc. + +It is recommended to grant a handler either Execute/Script + or Write + permissions, but not both. + + + + + + + Data + Protect + + + + + + + + + +By allowing both Execute/Script + and Write + permissions, a handler can run malicious code on the target server. Ensuring these two permissions are never together will help lower the risk of malicious code being executed on the server. + + + + http://technet.microsoft.com/en-us/library/dd391910%28WS.10%29.aspx + http://blogs.iis.net/thomad/archive/2006/11/05/quo-vadis-accessflags.aspx + + + + +The accessPolicy + attribute in the <handlers> + section of either the ApplicationHost.config + (server-wide) or web.config + (site or application) must not have Write + present when Script + or Execute + are present. To resolve this issue for a Web server, the attribute in the <handlers> + section of the ApplicationHost.config + file for the server must manually be edited. To edit the ApplicationHost.config file by using Notepad, perform the following steps: + + Open Notepad as Administrator + +Open the ApplicationHost.config file in %systemroot%\system32\inetsrv\config + + +Edit the <handlers> + section accessPolicy + attribute so that Write + is not present when Script + or Execute + are present + + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config /section:handlers /accessPolicy:Read,Script + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/handlers" -name "accessPolicy" -value "Read,Script" + + Note: This configuration change cannot be made by using IIS Manager. + Impact: + + N/A + + + + + + + (L1) Ensure 'notListedIsapisAllowed' is set to false + + +The notListedIsapisAllowed +attribute is a server-level setting that is located in the ApplicationHost.config + file in the <isapiCgiRestriction> + element of the <system.webServer> + section under <security> +. This element ensures that malicious users cannot copy unauthorized ISAPI binaries to the Web server and then run them. + +It is recommended that notListedIsapisAllowed + be set to false +. + + + + + + + + + + +Restricting this attribute to false +will help prevent potentially malicious ISAPI extensions from being run. + + + http://technet.microsoft.com/en-us/library/dd378846%28WS.10%29.aspx + http://www.iis.net/ConfigReference/system.webServer/security/isapiCgiRestriction + + + + +To use IIS Manager to set the notListedIsapisAllowed + attribute to false +: + + Open IIS Manager as Administrator + In the Connections pane on the left, select server to be configured + In Features View, select ISAPI and CGI Restrictions; in the Actions pane, select Open Feature + In the Actions pane, select Edit Feature Settings + In the Edit ISAPI and CGI Restrictions Settings dialog, clear the Allow unspecified ISAPI modules check box, if checked + Click OK + + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd.exe set config -section:system.webServer/security/isapiCgiRestriction /notListedIsapisAllowed:false + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/isapiCgiRestriction" -name "notListedIsapisAllowed" -value "False" + + Impact: + + Unauthorized ISAPI binaries will not be allowed. + + + + + + + + + + + + + (L1) Ensure 'notListedCgisAllowed' is set to false + + +The notListedCgisAllowed + attribute is a server-level setting that is located in the ApplicationHost.config + file in the <isapiCgiRestriction> + element of the <system.webServer> + section under <security> +. This element ensures that malicious users cannot copy unauthorized CGI binaries to the Web server and then run them. + +It is recommended that notListedCgisAllowed + be set to false +. + + + + + + + + + + +Restricting this attribute to false + will help prevent unlisted CGI extensions, including potentially malicious CGI scripts from being run. + + + http://technet.microsoft.com/en-us/library/dd391919%28WS.10%29.aspx + + + + +To set the notListedCgisAllowed + attribute to false using IIS Manager: + + Open IIS Manager as Administrator + In the Connections pane on the left, select the server to configure + In Features View, select ISAPI and CGI Restrictions; in the Actions pane, select Open Feature + In the Actions pane, select Edit Feature Settings + In the Edit ISAPI and CGI Restrictions Settings dialog, clear the Allow unspecified CGI modules check box + Click OK + + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd.exe set config -section:system.webServer/security/isapiCgiRestriction /notListedCgisAllowed:false + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/isapiCgiRestriction" -name "notListedCgisAllowed" -value "False" + + Impact: + + Unlisted CGI extensions will not be allowed. + + + + + + + + + + + + + (L1) Ensure 'Dynamic IP Address Restrictions' is enabled + + Dynamic IP address filtering allows administrators to configure the server to block access for IPs that exceed the specified number of requests or request frequency. + + Note: + Ensure that you receive the Forbidden page once the block has been enforced. + + + + + + + Network + Protect + + + + + + Devices + Protect + + + + + + IIS Dynamic IP Address Restrictions capability can be used to thwart DDos attacks. This is complimentary to the IP Addresses and Domain names Restrictions lists that can be manually maintained within IIS. In contrast, Dynamic IP address filtering allows administrators to configure the server to block access for IPs that exceed the specified request threshold. The default action Deny action for restrictions is to return a Forbidden response to the client. + + + + http://www.iis.net/learn/get-started/whats-new-in-iis-8/iis-80-dynamic-ip-address-restrictions + + + + + + Open IIS Manager. + + + Open the IP Address and Domain Restrictions feature. + + + Click Edit Dynamic Restrictions Settings.. + + + Check the Deny IP Address based on the number of concurrent requests and the Deny IP Address based on the number of requests over a period of time boxes. The values can be tweaked as needed for your specific environment. + + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/dynamicIpSecurity/denyByConcurrentRequests" -name "enabled" -value "True" + + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/security/dynamicIpSecurity/denyByConcurrentRequests" -name "maxConcurrentRequests" -value <number of requests> + + Impact: + + Clients will receive a forbidden response when the specified number of requests or request frequency is exceeded. + + + + + + + + IIS Logging Recommendations + + This section contains recommendations regarding IIS logging that have not been covered in the Basic Configurations section. + + + (L1) Ensure Default IIS web log location is moved + + IIS will log relatively detailed information on every request. These logs are usually the first item looked at in a security response and can be the most valuable. Malicious users are aware of this and will often try to remove evidence of their activities. + It is recommended that the default location for IIS log files be changed to a restricted, non-system drive. + + + + + + + Network + Protect + + + + + + Network + Detect + + + + + + Moving IIS logging to a restricted, non-system drive will help mitigate the risk of logs being maliciously altered, removed, or lost in the event of system drive failure(s). + + + + https://technet.microsoft.com/en-us/library/cc770709(v=ws.10).aspx? + + + + +Moving the default log location can be easily accomplished using the Logging feature in the IIS Management UI, AppCmd.exe +, or PowerShell. + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd set config -section:sites -siteDefaults.logfile.directory:<new log location> + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/sites/siteDefaults/logFile" -name "directory" -value <new log location> + + Moving log file stores to a non-system drive or partition separate from where web applications run and/or content is served is preferred. Additionally, folder-level NTFS permissions should be set as restrictive as possible; Administrators and SYSTEM are typically the only principals requiring access. + While standard IIS logs can be moved and edited using IIS Manager, additional management tool add-ons are required in order to manage logs generated by other IIS features, such as Request Filtering and IIS Advanced Logging. These add-ons can be obtained using the Web Platform Installer or from Microsoft's site. The HTTPErr logging location can be changed by adding a registry key. + Impact: + + If an administrator needs access to the log file, that does not have drive permission, they will be unable to view that file. + + + + + + + (L1) Ensure Advanced IIS logging is enabled + + IIS Advanced Logging is a module which provides flexibility in logging requests and client data. It provides controls that allow businesses to specify what fields are important, easily add additional fields, and provide policies pertaining to log file rollover and Request Filtering. HTTP request/response headers, server variables, and client-side fields can be easily logged with minor configuration in the IIS management console. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + Many of the fields available in Advanced Logging can provide extensive, real-time data and details not otherwise obtainable. Developers and security professionals can use this information to identify and remediate application vulnerabilities/attack patterns. + + + + + https://www.iis.net/learn/get-started/whats-new-in-iis-85/enhanced-logging-for-iis85 + + + + IIS Advanced Logging can be configured for servers, Web sites, and directories in IIS Manager. To enable Advanced Logging using the UI: + + Open Internet Information Services (IIS) Manager + Click the server in the Connections pane + Double-click the Logging icon on the Home page + Click Select Fields + + The fields that will be logged need to be configured using the Add or Edit Fields button. + Note: There may be performance considerations depending on the extent of the configuration. + Impact: + + Collecting detailed log files will take more space on the specified drive. + + + + + + + (L1) Ensure 'ETW Logging' is enabled + + Event Tracing for Windows (ETW) is a Windows feature that allows Administrators to send logging information to another location. This information is then compiled on the server and can be queried. + + + + + + + Network + Detect + + + + + + Network + Detect + + + + Network + Detect + + + + + + IIS flushes log information to disk, therefore prior to IIS, administrators do not have access to real-time logging information. Text-based log files can also be difficult and time consuming to process. By enabling ETW, administrators have access to use standard query tools for viewing real-time logging information. + + + + + http://www.iis.net/learn/get-started/whats-new-in-iis-85/logging-to-etw-in-iis-85 + http://blogs.technet.com/b/erezs_iis_blog/archive/2013/07/15/hook-me-up.aspx + https://blogs.msdn.microsoft.com/dcook/2015/09/30/etw-overview/ + https://social.msdn.microsoft.com/Forums/en-US/a1aa1350-41a0-4490-9ae3-9b4520aeb9d4/faq-common-questions-for-etw-and-windows-event-log?forum=etw + + + + To configure ETW logging: + + Open IIS Manager + Select the server or site to enable ETW + Select Logging. + Ensure Log file format is W3C. + Select Both log file and ETW event + Save your settings. + + Impact: + + A dedicated server hosting Event Tracing for Windows (ETW) will be needed. + + + + + + + + FTP Requests + + This section contains a crucial configuration setting for running file transfer protocol (FTP). + + + (L1) Ensure FTP requests are encrypted + + FTP Publishing Service for IIS supports adding an SSL certificate to an FTP site. Using an SSL certificate with an FTP site is also known as FTP-S or FTP over Secure Socket Layers (SSL). FTP-S is an RFC standard (RFC 4217) where an SSL certificate is added to an FTP site and thereby making it possible to perform secure file transfers. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + By using SSL, the FTP transmission is encrypted and secured from point to point and all FTP traffic as well as credentials are thereby guarded against interception. + + + + http://www.windowsnetworking.com/articles_tutorials/IIS-FTP-Publishing-Service-Part3.html + http://learn.iis.net/page.aspx/304/using-ftp-over-ssl/#03 + https://tools.ietf.org/html/rfc4217 + + + + To configure FTP over SSL at the server level using AppCmd.exe or PowerShell: + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd.exe set config -section:system.applicationHost/sites /siteDefaults.ftpServer.security.ssl.controlChannelPolicy:"SslRequire" /siteDefaults.ftpServer.security.ssl.dataChannelPolicy:"SslRequire" /commit:apphost + + OR + Enter the following commands in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/sites/siteDefaults/ftpServer/security/ssl" -name "controlChannelPolicy" -value "SslRequire" + + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/sites/siteDefaults/ftpServer/security/ssl" -name "dataChannelPolicy" -value "SslRequire" + + Impact: + + SSL will be needed for the FTP transmission. + + + + + + + (L1) Ensure FTP Logon attempt restrictions is enabled + + FTP Logon attempt restrictions is a built-in network security feature to automatically block brute force FTP attacks. This can be used to mitigate a malicious client from attempting a brute-force attack on a discovered account, such as the local administrator account. + + + + + + + Devices + Protect + + + + + + Devices + Identify + + + + + + Successful brute force FTP attacks can allow an otherwise unauthorized user to make changes to data that should not be made. This could allow the unauthorized user to modify website code by uploading malicious software or even changing functionality for items such as online payments. + + + + http://www.iis.net/learn/get-started/whats-new-in-iis-8/iis-80-ftp-logon-attempt-restrictions + + + + To configure FTP Logon Attempt Restrictions at the server level using AppCmd.exe or PowerShell: + Enter the following command in AppCmd.exe to configure: + %systemroot%\system32\inetsrv\appcmd.exe set config -section:system.ftpServer/security/authentication /denyByFailure.enabled:"True" /commit:apphost + + OR + Enter the following command in PowerShell to configure: + Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.ftpServer/security/authentication/denyByFailure" -name "enabled" -value "True" + + Impact: + + N/A + + + + + + + + Transport Encryption + + This section contains recommendations for configuring IIS protocols and cipher suites. + For security protocols (SSL, TLS), there are 2 registry paths that control a protocol state in the O/S: TLS client and TLS server. A web server normally acts as the TLS server in that it is serving web content to clients. There are some instances where a web server is configured as a 'client'. An example of a server acting as a client can be seen when there is dynamic content generation. The webserver queries a remote database server to return content specific to a user's request. In this configuration, the web server is acting as a TLS client. In cases such as these, the configured TLS server protocol and cipher suite preferences take precedence over the client's. This behavior is why for the IIS benchmark we require specific protocol settings for a TLS server and only recommend settings for TLS clients. + If SSLv3 registry keys are not set, the O/S defaults take precedence. + +For example, to disable SSLv3 protocol on the TLS server, you need to set the following registry key to 0: + HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server\Enabled + + +To prevent a client from issuing the Hello command over that legacy protocol the following registry must be set to 0: + HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client\Enabled + + The fact that the key is named Enabled can be confusing. The setting of the value to either 0 or 1 actually sets the state of the protocol. 0 being disabled and 1 being enabled. + +Here are some specifics into how "Enabled" and "DisabledByDefault" registry settings work. The following article, How to restrict the use of certain cryptographic algorithms and protocols in Schannel.dll +, provides additional information related to controlling these protocols and ciphers. + Using the "Enabled = 0" registry setting disables the protocol in a way that can't be overridden by application settings. This is the only robust way to prevent the protocol from being used and no additional settings are required. At the same time, using the "DisabledByDefault" registry setting only prevents that protocol from issuing the Hello command over that protocol when an SSL connection with a server is initiated. This O/S level setting can be overridden by an application which has application specific TLS coding. An example of this can be shown by setting the protocol within a line of code in your .Net 4.5 application: ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12. This can override the O/S setting if the DisabledByDefault key is present. "DisabledByDefault" is useful in the case when you want to have some control over the system settings but also allow an application to explicitly specify the protocols they would like to use. + Enabled only works strongly in the negative case ("Enabled = 0"). If "Enabled=1" or is not set, then "DisabledByDefault" will override in the case where the application takes the system defaults. "Enabled=1" is also overridden by application specific protocol flags. + + + (L2) Ensure HSTS Header is set + + HTTP Strict Transport Security (HSTS) allows a site to inform the user agent to communicate with the site only over HTTPS. This header takes two parameters: max-age, "specifies the number of seconds, after the reception of the STS header field, during which the user agent regards the host (from whom the message was received) as a Known HSTS Host [speaks only HTTPS]"; and includeSubDomains. includeSubDomains is an optional directive that defines how this policy is applied to subdomains. If includeSubDomains is included in the header, it provides the following definition: this HSTS Policy also applies to any hosts whose domain names are subdomains of the Known HSTS Host's domain name. + + + + + + + Data + Protect + + + + + + + + + HTTP Strict Transport Security (HSTS) is a simple and widely supported standard to protect visitors by ensuring that their browsers always connect to a website over HTTPS. HSTS exists to remove the need for the common, insecure practice of redirecting users from http:// to https:// URLs. HSTS relies on the User Agent/Browser to enforce the required behavior. All major browsers support it. If the browser doesn't support HSTS, it will be ignored. + When a browser knows that a domain has enabled HSTS, it does two things: + + + Always uses an https:// connection, even when clicking on an http:// link or after typing a domain into the location bar without specifying a protocol. + + + Removes the ability for users to click through warnings about invalid certificates. + + + A domain instructs browsers that it has enabled HSTS by returning an HTTP header over an HTTPS connection. + + + + http://tools.ietf.org/html/rfc6797#section-5.1 + https://https.cio.gov/hsts/ + https://www.iis.net/configreference/system.webserver/httpprotocol/customheaders#006 + + + + Any value greater than 0 meets this recommendation. The examples below are specific to 8 minutes but can be adjusted to meet your requirements. + +To set the HTTP Header at the server level using an AppCmd.exe + command, run the following command from an elevated command prompt: + %systemroot%\system32\inetsrv\appcmd.exe set config -section:system.webServer/httpProtocol /+"customHeaders.[name='Strict-Transport-Security',value='max-age=480; preload']" + + +To set the HTTP Header and include subdomains at the server level using an AppCmd.exe + command, run the following command from an elevated command prompt: + %systemroot%\system32\inetsrv\appcmd.exe set config -section:system.webServer/httpProtocol /+"customHeaders.[name='Strict-Transport-Security',value='max-age=480; includeSubDomains; preload']" + + +To set the HTTP Header at the Website level using an AppCmd.exe + command, run the following command from an elevated command prompt: + %systemroot%\system32\inetsrv\appcmd.exe set config "<em>Website"</em> -section:system.webServer/httpProtocol /+"customHeaders.[name='Strict-Transport-Security',value='max-age=480; preload']" + + +To set the HTTP Header and include subdomains at the Website level using an AppCmd.exe + command, run the following command from an elevated command prompt: + %systemroot%\system32\inetsrv\appcmd.exe set config "<em>Website"</em> -section:system.webServer/httpProtocol /+"customHeaders.[name='Strict-Transport-Security',value='max-age=480; includeSubDomains; preload']" + + Impact: + + The user agent will only be able to communicate with the site over HTTPS. + + + + + + + (L1) Ensure SSLv2 is Disabled + + The SSLv2 protocol is not considered cryptographically secure, therefore should be disabled. + + + + + + + Network + Protect + + + + + + Data + Protect + + + + + + Disabling weak protocols will help ensure the confidentiality and integrity of in-transit data. + + + + http://technet.microsoft.com/en-us/library/dn786419.aspx + http://technet.microsoft.com/en-us/library/dn786433.aspx + http://msdn.microsoft.com/en-us/library/aa374757%28v=vs.85%29.aspx + https://www.owasp.org/index.php/Testing_for_SSL-TLS_%28OWASP-CM-001%29 + + + + Perform the following to disable SSL 2.0: + + +Set the following Registry key to 0 +. + + +HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server:Enabled
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client:Enabled +
+ + +Set the following Registry key to 1 +. + + +HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server:DisabledByDefault
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client:DisabledByDefault +
+ To disable using PowerShell enter the following command: + +New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server' -Force | Out-Null
+
+New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server' -name 'DisabledByDefault' -value '1' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client' -name 'DisabledByDefault' -value '1' -PropertyType 'DWord' -Force | Out-Null +
+ Impact: + + The SSLv2 protocol will not be available for use. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + (L1) Ensure SSLv3 is Disabled + + The SSLv3 protocol is not considered cryptographically secure, therefore should be disabled. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Disabling weak protocols will help ensure the confidentiality and integrity of in-transit data. + + + + https://www.openssl.org/~bodo/ssl-poodle.pdf + http://technet.microsoft.com/en-us/library/dn786419.aspx + https://www.owasp.org/index.php/Testing_for_SSL-TLS_%28OWASP-CM-001%29 + http://technet.microsoft.com/en-us/library/dn786433.aspx + http://msdn.microsoft.com/en-us/library/aa374757%28v=vs.85%29.aspx + + + + Perform the following to disable SSL 3.0: + + +Set the following Registry key to 0 +. + + +HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server:Enabled
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client:Enabled +
+ + +Set the following Registry key to 1 +. + + +HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server:DisabledByDefault
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client:DisabledByDefault +
+ To disable using PowerShell enter the following command: + +New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server' -Force | Out-Null
+
+New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server' -name 'DisabledByDefault' -value '1' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client' -name 'DisabledByDefault' -value '1' -PropertyType 'DWord' -Force | Out-Null +
+ Impact: + + The SSLv3 protocol will not be available. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + (L1) Ensure TLS 1.0 is Disabled + + The TLS 1.0 protocol is not considered cryptographically secure, therefore should be disabled. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Disabling weak protocols will help ensure the confidentiality and integrity of in-transit data. + + + + http://msdn.microsoft.com/en-us/library/aa374757%28v=vs.85%29.aspx + https://www.owasp.org/index.php/Testing_for_SSL-TLS_%28OWASP-CM-001%29 + http://technet.microsoft.com/en-us/library/dn786419.aspx + http://technet.microsoft.com/en-us/library/dn786433.aspx + + + + Perform the following to disable TLS 1.0: + + +Set the following Registry key to 0 +. + + +HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server:Enabled
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client:Enabled +
+ + +Set the following Registry key to 1 +. + + +HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server:DisabledByDefault
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client:DisabledByDefault +
+ To disable using PowerShell enter the following command: + +New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -Force | Out-Null
+
+New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server' -name 'DisabledByDefault' -value '1' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client' -name 'DisabledByDefault' -value '1' -PropertyType 'DWord' -Force | Out-Null +
+ Impact: + + The TLS 1.0 protocol will not be available. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + (L1) Ensure TLS 1.1 is Disabled + + The TLS 1.1 protocol is not considered cryptographically secure, therefore should be disabled. + + + + + + + Network + Protect + + + + + + Data + Protect + + + + + + Disabling weak protocols will help ensure the confidentiality and integrity of in-transit data. + + + + http://technet.microsoft.com/en-us/library/dn786433.aspx + https://www.owasp.org/index.php/Testing_for_SSL-TLS_%28OWASP-CM-001%29 + http://technet.microsoft.com/en-us/library/dn786419.aspx + http://msdn.microsoft.com/en-us/library/aa374757%28v=vs.85%29.aspx + https://community.qualys.com/thread/16565-is-there-a-reason-for-still-having-tlsv11-enabled + + + + Perform the following to disable TLS 1.1: + + +Set the following Registry key to 0 +. + + +HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server:Enabled
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client:Enabled +
+ + +Set the following Registry key to 1 +. + + +HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server:DisabledByDefault
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client:DisabledByDefault +
+ To disable using PowerShell enter the following command: + +New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -Force | Out-Null
+
+New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server' -name 'DisabledByDefault' -value '1' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client' -name 'DisabledByDefault' -value '1' -PropertyType 'DWord' -Force | Out-Null +
+ Impact: + + TLS 1.1 may be needed for backward compatibility. + + Warning: + Fully test the application to ensure that backwards compatibility is not needed. If it is, build in exceptions as necessary for backwards compatibility. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + (L1) Ensure TLS 1.2 is Enabled + + TLS 1.2 is the most recent and mature protocol for protecting the confidentiality and integrity of HTTP traffic. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Enabling this protocol will help ensure the confidentiality and integrity of data in transit. + + + + http://msdn.microsoft.com/en-us/library/aa374757%28v=vs.85%29.aspx + https://www.owasp.org/index.php/Testing_for_SSL-TLS_%28OWASP-CM-001%29 + http://technet.microsoft.com/en-us/library/dn786419.aspx + http://technet.microsoft.com/en-us/library/dn786433.aspx + + + + Perform the following to enable TLS 1.2: + + +Set the following Registry key to 1 +. + + HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server:Enabled + + + +Set the following Registry key to 0 +. + + HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server:DisabledByDefault + + To enable using PowerShell enter the following command: + +New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -name 'Enabled' -value '1' -PropertyType 'DWord' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -name 'DisabledByDefault' -value '0' -PropertyType 'DWord' -Force | Out-Null +
+ Impact: + + N/A + +
+
+
+ + + + + + + + + + + + +
+ + (L1) Ensure NULL Cipher Suites is Disabled + + The NULL cipher does not provide data confidentiality or integrity, therefore it is recommended that the NULL cipher be disabled. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + By disabling the NULL cipher, there is a better chance of maintaining data confidentiality and integrity. + + + + https://www.owasp.org/index.php/Testing_for_SSL-TLS_%28OWASP-CM-001%29 + http://technet.microsoft.com/en-us/library/dn786419.aspx + http://technet.microsoft.com/en-us/library/dn786433.aspx + http://msdn.microsoft.com/en-us/library/aa374757%28v=vs.85%29.aspx + + + + +Perform the following to disable NULL + cipher: + + +Set the following Registry key to 0 +. + + HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\NULL:Enabled + + To disable using PowerShell enter the following command: + +New-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\NULL' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\NULL' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null +
+ Impact: + + The NULL cipher suite will not be available. + +
+
+
+ + + + + + + +
+ + (L1) Ensure DES Cipher Suites is Disabled + + The DES Cipher Suite is considered a weak symmetric-key cipher, therefore it is recommended that it be disabled. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + By disabling DES, there is a better chance of maintaining data confidentiality and integrity. + + + + https://www.owasp.org/index.php/Testing_for_SSL-TLS_%28OWASP-CM-001%29 + http://technet.microsoft.com/en-us/library/dn786433.aspx + http://technet.microsoft.com/en-us/library/dn786419.aspx + http://msdn.microsoft.com/en-us/library/aa374757%28v=vs.85%29.aspx + + + + +Perform the following to disable DES 56/56 + cipher: + + +Set the following Registry key to 0 +. + + HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\DES 56/56:Enabled + + To disable using PowerShell enter the following command: + +(Get-Item 'HKLM:\').OpenSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers', $true).CreateSubKey('DES 56/56')
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\DES 56/56' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null +
+ Impact: + + The DES Cipher Suite will not be avaiable. + +
+
+
+ + + + + + + +
+ + (L1) Ensure RC4 Cipher Suites is Disabled + + The RC4 Cipher Suites are considered insecure, therefore should be disabled. + + Note: + RC4 cipher enabled by default on Server 2012 and 2012 R2 is RC4 128/128. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + The use of RC4 may increase an adversaries ability to read sensitive information sent over SSL/TLS. + + + + http://msdn.microsoft.com/en-us/library/aa374757%28v=vs.85%29.aspx + http://technet.microsoft.com/en-us/library/dn786433.aspx + http://technet.microsoft.com/en-us/library/dn786419.aspx + https://www.owasp.org/index.php/Testing_for_SSL-TLS_%28OWASP-CM-001%29 + + + + +Perform the following to disable RC4 40/128 +, RC4 56/128 +, RC4 64/128 +, RC4 128/128 + ciphers: + + +Set the following Registry keys to 0 +. + + +HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 40/128:Enabled
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 56/128:Enabled
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 64/128:Enabled
+HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 128/128:Enabled +
+ To disable using PowerShell enter the following commands: + +(Get-Item 'HKLM:\').OpenSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers', $true).CreateSubKey('RC4 40/128')
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 40/128' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+(Get-Item 'HKLM:\').OpenSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers', $true).CreateSubKey('RC4 56/128')
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 56/128' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+(Get-Item 'HKLM:\').OpenSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers', $true).CreateSubKey('RC4 64/128')
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 64/128' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null
+
+(Get-Item 'HKLM:\').OpenSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers', $true).CreateSubKey('RC4 128/128')
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 128/128' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null +
+ Impact: + + The RC4 Cipher Suites will not be available. The use of RC4 in TLS and SSL could allow an attacker to perform man-in-the-middle attacks and recover plaintext from encrypted sessions. + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + (L1) Ensure AES 128/128 Cipher Suite is Disabled + + The AES 128/128 Cipher Suite is not considered secure and therefore should be disabled, if possible. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + This item is Scored for the following reasons and should be disabled: + + Enabling AES 256/256 is recommended. + This cipher does not suffer from known practical attacks. + + + + + http://technet.microsoft.com/en-us/library/dn786419.aspx + http://msdn.microsoft.com/en-us/library/aa374757%28v=vs.85%29.aspx + https://www.owasp.org/index.php/Testing_for_SSL-TLS_%28OWASP-CM-001%29 + http://technet.microsoft.com/en-us/library/dn786433.aspx + + + + +Perform the following to disable AES 128/128 + cipher: + + +Set the following Registry key to 0 +. + + HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\AES 128/128:Enabled + + To disable using PowerShell enter the following command: + +(Get-Item 'HKLM:\').OpenSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers', $true).CreateSubKey('AES 128/128')
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\AES 128/128' -name 'Enabled' -value '0' -PropertyType 'DWord' -Force | Out-Null +
+ Impact: + + + Warning: + Enabling AES 128/128 may be required for client compatibility. + +
+
+
+ + + + + + + +
+ + (L1) Ensure AES 256/256 Cipher Suite is Enabled + + AES 256/256 is the most recent and mature cipher suite for protecting the confidentiality and integrity of HTTP traffic. Enabling AES 256/256 is recommended. + + Note: + AES 256/256 is enabled by default starting with Server 2012 and 2012 R2. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Enabling this cipher will help ensure the confidentiality and integrity of data in transit. + + + + http://technet.microsoft.com/en-us/library/dn786419.aspx + http://msdn.microsoft.com/en-us/library/aa374757%28v=vs.85%29.aspx + https://www.owasp.org/index.php/Testing_for_SSL-TLS_%28OWASP-CM-001%29 + http://technet.microsoft.com/en-us/library/dn786433.aspx + + + + +Perform the following to enable AES 256/256 + cipher: + + +Set the following Registry key to 1 +. + + HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\AES 256/256:Enabled + + To enable using PowerShell enter the following command: + +(Get-Item 'HKLM:\').OpenSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers', $true).CreateSubKey('AES 256/256')
+
+New-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\AES 256/256' -name 'Enabled' -value '1' -PropertyType 'DWord' -Force | Out-Null +
+ Impact: + + N/A + +
+
+
+ + + + + + + +
+ + (L2) Ensure TLS Cipher Suite ordering is Configured + + Cipher suites are a named combination of authentication, encryption, message authentication code, and key exchange algorithms used for the security settings of a network connection using TLS protocol. Clients send a cipher list and a list of ciphers that it supports in order of preference to a server. The server then replies with the cipher suite that it selects from the client cipher suite list. + + + + + + + Data + Protect + + + + + + Data + Protect + + + + + + Cipher suites should be ordered from strongest to weakest in order to ensure that the more secure configuration is used for encryption between the server and client. + + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + + + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + + + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + + + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + Avoid cipher suits that do not provide Perfect Forward Secrecy or use weak hashing function, use them only if you need to support backwards compatibility and in the bottom of the list and you will have to create exceptions for the items that cause this to become out of compliance: + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (uses SHA-1) + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (uses SHA-1) + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (uses SHA-1) + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (uses SHA-1) + TLS_RSA_WITH_AES_256_GCM_SHA384 (lack of Perfect Forward Secrecy) + TLS_RSA_WITH_AES_128_GCM_SHA256 (lack of Perfect Forward Secrecy) + TLS_RSA_WITH_AES_256_CBC_SHA256 (lack of Perfect Forward Secrecy) + TLS_RSA_WITH_AES_128_CBC_SHA256 (lack of Perfect Forward Secrecy) + TLS_RSA_WITH_AES_256_CBC_SHA (uses SHA-1, lack of Perfect Forward Secrecy) + TLS_RSA_WITH_AES_128_CBC_SHA (uses SHA-1, lack of Perfect Forward Secrecy) + + Note: + HTTP/2 compatibility: first 4 ciphers (in bold) in the top part list are compatible with HTTP/2 + + + + + + + Perform the following to configure TLS cipher suite order: + + +Set the following Registry key to TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 +. + + HKLM\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002:Functions + + To configure TLS cipher suite order using PowerShell enter the following command: + +New-Item 'HKLM:\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002' -Force | Out-Null
+
+New-ItemProperty -path 'HKLM:\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002' -name 'Functions' -value 'TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256' -PropertyType 'MultiString' -Force | Out-Null +
+ Impact: + + Cipher ordering is important to ensure that the most secure ciphers are listed first and will be applied over weaker ciphers when possible. + +
+
+
+ + + + + + + +
+
+ + + + + + + + + + +1dgQoBBPyJNtO8er2cY9rcIvtPo= + + + +ajiQa7zgKiX3poxqNSgP90uXuvf0EdaDw0ahYZiDunKJtVP1CloINpRKsMkU3pvfJkbOKAlHpaVV +W6r67Vc5dLymm5TxpZexXlB+Dbgvemf8AHYdMQoI6JxvIkZvM/vKVjTjryqO2KQgJBBLheijQExj +ruwDNrzknPt9TWAqFF+BCltyA1+H94eMWyg3PHweVmRzScGAmEeifPqv0wEbIjbLFCS80TXR7iNG +hXtM0LelkyxOxVubzKto7EkYpqUrzyB1tuP8Ewv0irBMFIEipCjwe8y49lAlPkPqLAXn72/N7NDI +/Oyhye9E0VHhpe/PEaBHQtph+N3qM2K0iTJbtg== + + + + +MIIFUjCCBDqgAwIBAgIIPx+qZKptEY4wDQYJKoZIhvcNAQELBQAwgbQxCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNv +bSwgSW5jLjEtMCsGA1UECxMkaHR0cDovL2NlcnRzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvMTMw +MQYDVQQDEypHbyBEYWRkeSBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwHhcNMjAw +OTA4MTMzOTM5WhcNMjMxMDI4MDAzMjA1WjCBkzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZ +b3JrMRcwFQYDVQQHEw5FYXN0IEdyZWVuYnVzaDErMCkGA1UEChMiQ2VudGVyIGZvciBJbnRlcm5l +dCBTZWN1cml0eSwgSW5jLjErMCkGA1UEAxMiQ2VudGVyIGZvciBJbnRlcm5ldCBTZWN1cml0eSwg +SW5jLjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANDBp4+sl9kB5JRmnQH0wcbLLz/t +n2HoD6TRl3xJj12V61njBNgaiGzvoSl07sFLnESukb0CU44MzYDoUMdx3HJUcEAeMY/9eITqGUnk +N4WsP145eFqH7FoszOY/4NYlgYMYt8ZP2kRHyim9LpQTLT/1mBaUkY2f2e5uUCootH3iT8W3/sK2 +flLzWfLgXTvkhJm4Uvw1RErBFs15+krW0KWtnZ6NmiDhmV0CnFGNnd1meGZPs2sBXEVGLJVSKzoT +G9Ht/oWyRLRcJuXpHoXTYpkqM2MsixmSaJy5b6/D35r0NXHru3p1gkX2a0VgJQxxWqLi3bVPzpl3 +PlJu3C8M/FUCAwEAAaOCAYUwggGBMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMw +DgYDVR0PAQH/BAQDAgeAMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwuZ29kYWRkeS5jb20v +Z2RpZzJzNS02LmNybDBdBgNVHSAEVjBUMEgGC2CGSAGG/W0BBxcCMDkwNwYIKwYBBQUHAgEWK2h0 +dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeS8wCAYGZ4EMAQQBMHYGCCsG +AQUFBwEBBGowaDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZ29kYWRkeS5jb20vMEAGCCsGAQUF +BzAChjRodHRwOi8vY2VydGlmaWNhdGVzLmdvZGFkZHkuY29tL3JlcG9zaXRvcnkvZ2RpZzIuY3J0 +MB8GA1UdIwQYMBaAFEDCvSeOzDSDMKIz1/tss/C0LIDOMB0GA1UdDgQWBBT7fOf4ISDprbE+qUFe ++friFTKgHjANBgkqhkiG9w0BAQsFAAOCAQEAT0XLw2vmuq6e58bkLDXdqKeGQjREDChLw2Mae9LI +JB93QgdA2OCLA1FwU2s7lawEDcma9wPiugxVGoaQNOfRw2/+cz0xmR3uaKQq6I24dWbHF20N2tSz +1RAAK1lRZKewA1fj+qqKWjzETA0LpqZlaFF+CKBpQcyfYyQRZxpHkB1vvKMf4kMIRKruvl4ixLjN +uAZrbQUWlXuq/MUkVP3FgdxWBPfmMGyngHs4hNMaSQGb1TEhBjj+PGoSunAyaOgO/P94XJPQQX3O +2oNDsPou6gKcNtbZrH+iYTJ8rcqQTontMwSP/HrYj8lgG3neG53vk05p0jCn2M5W0ukJ4LPEnA== + + + + +0MGnj6yX2QHklGadAfTBxssvP+2fYegPpNGXfEmPXZXrWeME2BqIbO+hKXTuwUucRK6RvQJTjgzN +gOhQx3HcclRwQB4xj/14hOoZSeQ3haw/Xjl4WofsWizM5j/g1iWBgxi3xk/aREfKKb0ulBMtP/WY +FpSRjZ/Z7m5QKii0feJPxbf+wrZ+UvNZ8uBdO+SEmbhS/DVESsEWzXn6StbQpa2dno2aIOGZXQKc +UY2d3WZ4Zk+zawFcRUYslVIrOhMb0e3+hbJEtFwm5ekehdNimSozYyyLGZJonLlvr8PfmvQ1ceu7 +enWCRfZrRWAlDHFaouLdtU/OmXc+Um7cLwz8VQ== +AQAB + + + +
\ No newline at end of file From baeac9f70e7856c344a9fee536708a4ff8a422e9 Mon Sep 17 00:00:00 2001 From: George M Dias Date: Wed, 12 Mar 2025 20:09:58 -0500 Subject: [PATCH 3/4] mocha version 10 to 11 Signed-off-by: George M Dias --- src/utils/emasser/apiConfig.ts | 2 +- src/utils/emasser/initConnection.ts | 2 +- src/utils/threshold.ts | 2 +- test/commands/attest/apply.test.ts | 81 +++++++++++++++-------------- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/src/utils/emasser/apiConfig.ts b/src/utils/emasser/apiConfig.ts index 47ea531f5..25cdd8523 100644 --- a/src/utils/emasser/apiConfig.ts +++ b/src/utils/emasser/apiConfig.ts @@ -36,7 +36,7 @@ function printHelpMessage() { * variables are missing, it prints an error message and exits the process. */ export class ApiConfig { - private envConfig: {[key: string]: string | undefined}; + private envConfig: {[key: string]: string | undefined}; // skipcq: JS-0368 public url: string; public port: number|any; diff --git a/src/utils/emasser/initConnection.ts b/src/utils/emasser/initConnection.ts index b9321c529..e0014f4cf 100644 --- a/src/utils/emasser/initConnection.ts +++ b/src/utils/emasser/initConnection.ts @@ -37,7 +37,7 @@ import globalAxios, {AxiosInstance, AxiosRequestConfig} from 'axios' * It supports client certificate authentication and allows configuring SSL verification. */ export class InitConnections { - private axiosRequestConfig: AxiosRequestConfig; + private axiosRequestConfig: AxiosRequestConfig; // skipcq: JS-0368 public configuration: Configuration; public axiosInstances: AxiosInstance diff --git a/src/utils/threshold.ts b/src/utils/threshold.ts index 69753f074..3c6602978 100644 --- a/src/utils/threshold.ts +++ b/src/utils/threshold.ts @@ -230,7 +230,7 @@ function cklControlStatus(control: ContextualizedControl, for_summary?: boolean) } function controlFindingDetails(control: {message: string[]}, controlCKLStatus: 'Not_Applicable' | 'Profile_Error' | 'Open' | 'NotAFinding' | 'Not_Reviewed') { - control.message.sort() + control.message.sort((a, b) => a.localeCompare(b)) switch (controlCKLStatus) { case 'Open': { return `One or more of the automated tests failed or was inconclusive for the control \n\n ${control.message.join('\n')}` diff --git a/test/commands/attest/apply.test.ts b/test/commands/attest/apply.test.ts index 341b9a9c6..8a3992717 100644 --- a/test/commands/attest/apply.test.ts +++ b/test/commands/attest/apply.test.ts @@ -1,40 +1,41 @@ -/* eslint-disable array-bracket-newline */ + /* eslint-disable array-element-newline */ -import { expect } from 'chai'; -import { runCommand } from '@oclif/test'; -import tmp from 'tmp'; -import path from 'path'; -import fs from 'fs'; -import { omitHDFChangingFields } from '../utils'; +import {expect} from 'chai' +import {before, after} from 'mocha' +import {runCommand} from '@oclif/test' +import tmp from 'tmp' +import path from 'path' +import fs from 'fs' +import {omitHDFChangingFields} from '../utils' + +function readAndParseJSON(filePath: string): any { + return JSON.parse(fs.readFileSync(filePath, 'utf8').replaceAll(/\r/gi, '')) +} describe('Test attest apply', () => { - let tmpobj; + let tmpobj: tmp.DirResult before(() => { - tmpobj = tmp.dirSync({ unsafeCleanup: true }); - }); + tmpobj = tmp.dirSync({unsafeCleanup: true}) + }) after(() => { - tmpobj.removeCallback(); - }); + tmpobj.removeCallback() + }) const captureOpts = { print: true, stripAnsi: false, - }; - - const readAndParseJSON = (filePath) => { - return JSON.parse(fs.readFileSync(filePath, 'utf8').replaceAll(/\r/gi, '')); - }; + } - const runAndValidate = async (commandArgs, outputFilePath, expectedFilePath) => { - const { stderr } = await runCommand(commandArgs, undefined, captureOpts); - const output = readAndParseJSON(outputFilePath); - const expected = readAndParseJSON(expectedFilePath); + const runAndValidate = async (commandArgs: string | string[], outputFilePath: string, expectedFilePath: string) => { + const {stderr} = await runCommand(commandArgs, undefined, captureOpts) + const output = readAndParseJSON(outputFilePath) + const expected = readAndParseJSON(expectedFilePath) - expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected)); - expect(stderr).to.be.empty; - }; + expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected)) + expect(stderr).to.be.empty // skipcq: JS-0354 + } it('Successfully applies a JSON attestations file', async () => { await runAndValidate( @@ -45,9 +46,9 @@ describe('Test attest apply', () => { '-o', `${tmpobj.name}/rhel8_attestations_jsonOutput.json`, ], `${tmpobj.name}/rhel8_attestations_jsonOutput.json`, - path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json') - ); - }); + path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), + ) + }) it('Successfully applies an XLSX attestations file', async () => { await runAndValidate( @@ -58,9 +59,9 @@ describe('Test attest apply', () => { '-o', `${tmpobj.name}/rhel8_attestations_xlsxOutput.json`, ], `${tmpobj.name}/rhel8_attestations_xlsxOutput.json`, - path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json') - ); - }); + path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), + ) + }) it('Successfully applies a YAML attestations file', async () => { await runAndValidate( @@ -71,9 +72,9 @@ describe('Test attest apply', () => { '-o', `${tmpobj.name}/rhel8_attestations_yamlOutput.json`, ], `${tmpobj.name}/rhel8_attestations_yamlOutput.json`, - path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json') - ); - }); + path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), + ) + }) it('Successfully applies JSON attestations file to an overlay profile', async () => { await runAndValidate( @@ -84,9 +85,9 @@ describe('Test attest apply', () => { '-o', `${tmpobj.name}/triple_overlay_attested.json`, ], `${tmpobj.name}/triple_overlay_attested.json`, - path.resolve('./test/sample_data/attestations/triple_overlay_attested.json') - ); - }); + path.resolve('./test/sample_data/attestations/triple_overlay_attested.json'), + ) + }) it('Successfully applies a YAML attestations file to an overlay profile', async () => { await runAndValidate( @@ -97,7 +98,7 @@ describe('Test attest apply', () => { '-o', `${tmpobj.name}/triple_overlay_attested_with_yml.json`, ], `${tmpobj.name}/triple_overlay_attested_with_yml.json`, - path.resolve('./test/sample_data/attestations/triple_overlay_attested.json') - ); - }); -}); + path.resolve('./test/sample_data/attestations/triple_overlay_attested.json'), + ) + }) +}) From 1bc3015ebf0fc77eedce803b8e71a2cbb4518864 Mon Sep 17 00:00:00 2001 From: George M Dias Date: Thu, 13 Mar 2025 16:27:21 -0500 Subject: [PATCH 4/4] some changes, but still not working Signed-off-by: George M Dias --- .eslintrc | 32 - eslint.config.js | 26 + package-lock.json | 3292 ++++++++++++------ package.json | 18 +- src/commands/attest/apply.ts | 112 +- src/commands/attest/create.ts | 210 +- src/commands/convert/asff2hdf.ts | 2 +- src/commands/convert/ckl2POAM.ts | 1 - src/commands/convert/hdf2csv.ts | 221 +- src/commands/emasser/configure.ts | 2 +- src/commands/emasser/get/controls.ts | 1 + src/commands/emasser/get/dashboards.ts | 115 +- src/commands/supplement/passthrough/read.ts | 32 +- src/commands/supplement/passthrough/write.ts | 96 +- src/commands/supplement/target/read.ts | 32 +- src/commands/supplement/target/write.ts | 96 +- 16 files changed, 2764 insertions(+), 1524 deletions(-) delete mode 100644 .eslintrc create mode 100644 eslint.config.js diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index dd7ef9e7e..000000000 --- a/.eslintrc +++ /dev/null @@ -1,32 +0,0 @@ -{ - "extends": [ - "oclif", - "oclif-typescript" - ], - "rules": { - "@typescript-eslint/no-unused-vars": "off", - "semi": "off", - "@typescript-eslint/semi": ["warn", "never"], - "unicorn/filename-case": "off", - "unicorn/prefer-node-protocol": "off", - "unicorn/numeric-separators-style": "off", - "unicorn/no-hex-escape": "off", - "unicorn/better-regex": "off", - "unicorn/no-zero-fractions": "off", - "unicorn/no-array-for-each": "off", - "unicorn/explicit-length-check": "off", - "unicorn/no-process-exit": "off", - "no-process-exit": "off", - "no-await-in-loop": "off", - "no-control-regex": "off", - "max-nested-callbacks": "off", - "unicorn/prefer-json-parse-buffer": "off", - "camelcase": "off", - "no-console": "off", - "no-unused-expressions": "off", - "node/no-missing-import": "off", - "complexity": "off", - "no-constant-condition": "off", - "indent": ["error", 2, { "SwitchCase": 1 }] - } -} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..2170d2d8c --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,26 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2020, // Enable ES2020 features + sourceType: 'module', // Allow ES modules + }, + extends: [ + 'oclif', + 'oclif-typescript', + 'plugin:@typescript-eslint/recommended', // Include recommended settings for TypeScript + ], + plugins: ['@typescript-eslint'], // Use the TypeScript plugin + env: { + node: true, // Enable Node.js global variables and scope + es2020: true, // Enable ES2020 globals + }, + overrides: [ + { + files: ['src/**/*.ts'], // Target all `.ts` files in the `src` directory + rules: { + // You can add or customize rules here + '@typescript-eslint/explicit-module-boundary-types': 'off', // Example rule customization + }, + }, + ], +}; diff --git a/package-lock.json b/package-lock.json index 56082dcb0..a2e688d89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -90,22 +90,27 @@ }, "devDependencies": { "@e965/xlsx": "^0.20.0", + "@eslint/js": "^9.22.0", "@oclif/test": "^4.1.0", "@types/js-yaml": "^4.0.9", "@types/jsdom": "^21.1.7", "@types/mock-fs": "^4.13.4", - "@typescript-eslint/eslint-plugin": "~7.18.0", - "eslint": "^8.48.0", + "@typescript-eslint/eslint-plugin": "^8.26.1", + "@typescript-eslint/parser": "^8.26.1", + "eslint": "^8.57.1", "eslint-config-oclif": "^4.0", - "eslint-config-oclif-typescript": "^1.0.3", - "eslint-plugin-unicorn": "^56.0.0", + "eslint-config-oclif-typescript": "^3.1.14", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-unicorn": "^56.0.1", + "globals": "^16.0.0", "jsdom": "^26.0.0", "marked": "^15.0.0", "mocha": "^11", "mock-fs": "^5.2.0", "oclif": "^4.8.8", "tmp": "^0.2.1", - "ts-mocha": "^11.1.0" + "ts-mocha": "^11.1.0", + "typescript-eslint": "^8.26.1" }, "engines": { "node": "^22.0.0" @@ -1508,83 +1513,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", - "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "peer": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", @@ -2106,6 +2034,22 @@ "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2124,13 +2068,27 @@ "node": "*" } }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "version": "9.22.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz", + "integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@ewoudenberg/difflib": { @@ -3590,6 +3548,15 @@ "node": ">= 8" } }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "engines": { + "node": ">=12.4.0" + } + }, "node_modules/@oclif/core": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.2.10.tgz", @@ -3771,6 +3738,12 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -4732,6 +4705,12 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, "node_modules/@types/jsonfile": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", @@ -4849,6 +4828,12 @@ "resolved": "https://registry.npmjs.org/@types/revalidator/-/revalidator-0.3.12.tgz", "integrity": "sha512-DsA2jHfz73JaIROVoMDd/x7nVWXBmEdDSoXB4yQlDzv/NCBkFY2fMHkyE6DGrvooLDAFe5QI6l9Wq0TgdopMtg==" }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -4927,346 +4912,215 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz", + "integrity": "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/type-utils": "8.26.1", + "@typescript-eslint/utils": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", - "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", + "node_modules/@typescript-eslint/parser": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz", + "integrity": "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", + "debug": "^4.3.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz", + "integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz", + "integrity": "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==", "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.26.1", + "@typescript-eslint/utils": "8.26.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "node_modules/@typescript-eslint/types": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz", + "integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==", "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz", + "integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "node_modules/@typescript-eslint/utils": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", + "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz", + "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" + "@typescript-eslint/types": "8.26.1", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", + "integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==", "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=14.6" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true - }, - "node_modules/@xmldom/xmldom": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", - "integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==", - "engines": { - "node": ">=14.6" - } - }, - "node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -5435,11 +5289,47 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -5448,6 +5338,83 @@ "node": ">=8" } }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -5478,6 +5445,15 @@ "csv": "^5.1.3" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/async-retry": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", @@ -5492,6 +5468,21 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/axios": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", @@ -5832,6 +5823,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -5881,6 +5881,24 @@ "node": ">=14.16" } }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -6612,6 +6630,57 @@ "node": ">=18" } }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -6746,6 +6815,23 @@ "node": ">=10" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -6757,6 +6843,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -7079,18 +7182,17 @@ "once": "^1.4.0" } }, - "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dev": true, - "peer": true, "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": ">=8.6" + "node": ">=10.13.0" } }, "node_modules/entities": { @@ -7112,10 +7214,75 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -7153,6 +7320,35 @@ "node": ">= 0.4" } }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -7249,112 +7445,53 @@ } }, "node_modules/eslint-config-oclif-typescript": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-1.0.3.tgz", - "integrity": "sha512-TeJKXWBQ3uKMtzgz++UFNWpe1WCx8mfqRuzZy1LirREgRlVv656SkVG4gNZat5rRNIQgfDmTS+YebxK02kfylA==", + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-3.1.14.tgz", + "integrity": "sha512-YeBq5OiDRZFvfZ+wO0meF38fV06+zmEg15mnOLwkiAuUhjg2lH+UxvYA7uX2zUwR4p1WMUbfX+7CMfUwQ4TQ1A==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "^4.31.2", - "@typescript-eslint/parser": "^4.31.2", - "eslint-config-xo-space": "^0.29.0", - "eslint-plugin-mocha": "^9.0.0", - "eslint-plugin-node": "^11.1.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "peer": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "peer": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint-config-xo-space": "^0.35.0", + "eslint-import-resolver-typescript": "^3.7.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-mocha": "^10.5.0", + "eslint-plugin-n": "^15", + "eslint-plugin-perfectionist": "^2.11.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.0.0" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "peer": true - }, "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -7363,25 +7500,26 @@ } }, "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -7390,29 +7528,56 @@ } }, "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -7420,21 +7585,22 @@ } }, "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -7446,356 +7612,447 @@ } } }, - "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "peer": true, - "bin": { - "acorn": "bin/acorn" }, - "engines": { - "node": ">=0.4.0" + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, - "peer": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/eslint-config-oclif-typescript/node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/eslint-config-oclif-typescript/node_modules/eslint-config-xo-space": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/eslint-config-xo-space/-/eslint-config-xo-space-0.35.0.tgz", + "integrity": "sha512-+79iVcoLi3PvGcjqYDpSPzbLfqYpNcMlhsCBRsnmDoHAn4npJG6YxmHpelQKpXM7v/EeZTUKb4e1xotWlei8KA==", "dev": true, - "peer": true, "dependencies": { - "color-convert": "^2.0.1" + "eslint-config-xo": "^0.44.0" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "peer": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "url": "https://github.com/sponsors/sindresorhus" + }, + "peerDependencies": { + "eslint": ">=8.56.0" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint-config-oclif-typescript/node_modules/eslint-config-xo-space/node_modules/eslint-config-xo": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/eslint-config-xo/-/eslint-config-xo-0.44.0.tgz", + "integrity": "sha512-YG4gdaor0mJJi8UBeRJqDPO42MedTWYMaUyucF5bhm2pi/HS98JIxfFQmTLuyj6hGpQlAazNfyVnn7JuDn+Sew==", "dev": true, - "peer": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "confusing-browser-globals": "1.0.11" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" + }, + "peerDependencies": { + "eslint": ">=8.56.0" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/eslint-config-oclif-typescript/node_modules/eslint-plugin-mocha": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.5.0.tgz", + "integrity": "sha512-F2ALmQVPT1GoP27O1JTZGrV9Pqg8k79OeIuvw63UxMtQKREZtmkK1NFgkZQ2TW7L2JSSFKHFPTtHu5z8R9QNRw==", "dev": true, - "peer": true, "dependencies": { - "color-name": "~1.1.4" + "eslint-utils": "^3.0.0", + "globals": "^13.24.0", + "rambda": "^7.4.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "peer": true - }, - "node_modules/eslint-config-oclif-typescript/node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "node_modules/eslint-config-oclif-typescript/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" + "type-fest": "^0.20.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=8" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/eslint-config-xo": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/eslint-config-xo/-/eslint-config-xo-0.38.0.tgz", - "integrity": "sha512-G2jL+VyfkcZW8GoTmqLsExvrWssBedSoaQQ11vyhflDeT3csMdBVp0On+AVijrRuvgmkWeDwwUL5Rj0qDRHK6g==", + "node_modules/eslint-config-oclif-typescript/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { - "confusing-browser-globals": "1.0.10" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - }, - "peerDependencies": { - "eslint": ">=7.20.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/eslint-config-xo-space": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/eslint-config-xo-space/-/eslint-config-xo-space-0.29.0.tgz", - "integrity": "sha512-emUZVHjmzl3I1aO2M/2gEpqa/GHXTl7LF/vQeAX4W+mQIU+2kyqY97FkMnSc2J8Osoq+vCSXCY/HjFUmFIF/Ag==", + "node_modules/eslint-config-oclif-typescript/node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, - "dependencies": { - "eslint-config-xo": "^0.38.0" + "engines": { + "node": ">=16" }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-config-oclif/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-config-oclif/node_modules/eslint-plugin-unicorn": { + "version": "36.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-36.0.0.tgz", + "integrity": "sha512-xxN2vSctGWnDW6aLElm/LKIwcrmk6mdiEcW55Uv5krcrVcIFSWMmEgc/hwpemYfZacKZ5npFERGNz4aThsp1AA==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.14.9", + "ci-info": "^3.2.0", + "clean-regexp": "^1.0.0", + "eslint-template-visitor": "^2.3.2", + "eslint-utils": "^3.0.0", + "is-builtin-module": "^3.1.0", + "lodash": "^4.17.21", + "pluralize": "^8.0.0", + "read-pkg-up": "^7.0.1", + "regexp-tree": "^0.1.23", + "safe-regex": "^2.1.1", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" }, "peerDependencies": { "eslint": ">=7.32.0" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/eslint-config-xo": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/eslint-config-xo/-/eslint-config-xo-0.35.0.tgz", + "integrity": "sha512-+WyZTLWUJlvExFrBU/Ldw8AB/S0d3x+26JQdBWbcqig2ZaWh0zinYcHok+ET4IoPaEcRRf3FE9kjItNVjBwnAg==", "dev": true, - "peer": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "confusing-browser-globals": "1.0.10" }, "engines": { - "node": ">=8.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + }, + "peerDependencies": { + "eslint": ">=7.20.0" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "node_modules/eslint-config-xo-space": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/eslint-config-xo-space/-/eslint-config-xo-space-0.27.0.tgz", + "integrity": "sha512-b8UjW+nQyOkhiANVpIptqlKPyE7XRyQ40uQ1NoBhzVfu95gxfZGrpliq8ZHBpaOF2wCLZaexTSjg7Rvm99vj4A==", "dev": true, - "peer": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "eslint-config-xo": "^0.35.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://github.com/sponsors/sindresorhus" + }, + "peerDependencies": { + "eslint": ">=7.20.0" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, - "peer": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.8.6.tgz", + "integrity": "sha512-d9UjvYpj/REmUoZvOtDEmayPlwyP4zOwwMBgtC6RtrpZta8u1AIVmxgZBYJIcCKKXwAcLs+DX2yn2LeMaTqKcQ==", + "dev": true, + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.3.7", + "enhanced-resolve": "^5.15.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^1.0.2", + "stable-hash": "^0.0.4", + "tinyglobby": "^0.2.12" + }, "engines": { - "node": ">=4" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } } }, - "node_modules/eslint-config-oclif-typescript/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, "engines": { - "node": ">=10" + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/eslint-config-oclif-typescript/node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "peer": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, "engines": { - "node": ">= 4" + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, - "peer": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "eslint-visitor-keys": "^1.1.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/espree/node_modules/eslint-visitor-keys": { + "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, - "peer": true, "engines": { "node": ">=4" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, - "peer": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, "engines": { - "node": ">=4.0" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "peer": true, "dependencies": { - "is-glob": "^4.0.1" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/eslint-plugin-import/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "peer": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "minimist": "^1.2.0" }, "bin": { - "js-yaml": "bin/js-yaml.js" + "json5": "lib/cli.js" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true - }, - "node_modules/eslint-config-oclif-typescript/node_modules/minimatch": { + "node_modules/eslint-plugin-import/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7803,103 +8060,91 @@ "node": "*" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/eslint-config-oclif/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "node_modules/eslint-plugin-import/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/eslint-config-oclif/node_modules/eslint-plugin-unicorn": { - "version": "36.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-36.0.0.tgz", - "integrity": "sha512-xxN2vSctGWnDW6aLElm/LKIwcrmk6mdiEcW55Uv5krcrVcIFSWMmEgc/hwpemYfZacKZ5npFERGNz4aThsp1AA==", + "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/eslint-plugin-mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-9.0.0.tgz", + "integrity": "sha512-d7knAcQj1jPCzZf3caeBIn3BnW6ikcvfz0kSqQpwPYcVGLoJV5sz0l0OJB2LR8I7dvTDbqq1oV6ylhSgzA10zg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.14.9", - "ci-info": "^3.2.0", - "clean-regexp": "^1.0.0", - "eslint-template-visitor": "^2.3.2", "eslint-utils": "^3.0.0", - "is-builtin-module": "^3.1.0", - "lodash": "^4.17.21", - "pluralize": "^8.0.0", - "read-pkg-up": "^7.0.1", - "regexp-tree": "^0.1.23", - "safe-regex": "^2.1.1", - "semver": "^7.3.5" + "ramda": "^0.27.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" + "node": ">=12.0.0" }, "peerDependencies": { - "eslint": ">=7.32.0" + "eslint": ">=7.0.0" } }, - "node_modules/eslint-config-xo": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/eslint-config-xo/-/eslint-config-xo-0.35.0.tgz", - "integrity": "sha512-+WyZTLWUJlvExFrBU/Ldw8AB/S0d3x+26JQdBWbcqig2ZaWh0zinYcHok+ET4IoPaEcRRf3FE9kjItNVjBwnAg==", + "node_modules/eslint-plugin-n": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", + "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==", "dev": true, "dependencies": { - "confusing-browser-globals": "1.0.10" + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", + "ignore": "^5.1.1", + "is-core-module": "^2.11.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.1", + "semver": "^7.3.8" }, "engines": { - "node": ">=10" + "node": ">=12.22.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/mysticatea" }, "peerDependencies": { - "eslint": ">=7.20.0" + "eslint": ">=7.0.0" } }, - "node_modules/eslint-config-xo-space": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/eslint-config-xo-space/-/eslint-config-xo-space-0.27.0.tgz", - "integrity": "sha512-b8UjW+nQyOkhiANVpIptqlKPyE7XRyQ40uQ1NoBhzVfu95gxfZGrpliq8ZHBpaOF2wCLZaexTSjg7Rvm99vj4A==", + "node_modules/eslint-plugin-n/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "eslint-config-xo": "^0.35.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - }, - "peerDependencies": { - "eslint": ">=7.20.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "node_modules/eslint-plugin-n/node_modules/eslint-plugin-es": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", "dev": true, "dependencies": { "eslint-utils": "^2.0.0", @@ -7915,7 +8160,7 @@ "eslint": ">=4.19.1" } }, - "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "node_modules/eslint-plugin-n/node_modules/eslint-plugin-es/node_modules/eslint-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", @@ -7930,7 +8175,7 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { + "node_modules/eslint-plugin-n/node_modules/eslint-visitor-keys": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", @@ -7939,20 +8184,16 @@ "node": ">=4" } }, - "node_modules/eslint-plugin-mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-9.0.0.tgz", - "integrity": "sha512-d7knAcQj1jPCzZf3caeBIn3BnW6ikcvfz0kSqQpwPYcVGLoJV5sz0l0OJB2LR8I7dvTDbqq1oV6ylhSgzA10zg==", + "node_modules/eslint-plugin-n/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "eslint-utils": "^3.0.0", - "ramda": "^0.27.1" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "node": "*" } }, "node_modules/eslint-plugin-node": { @@ -7991,43 +8232,184 @@ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-node/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-node/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-perfectionist": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-2.11.0.tgz", + "integrity": "sha512-XrtBtiu5rbQv88gl+1e2RQud9te9luYNvKIgM9emttQ2zutHPzY/AQUucwxscDKV4qlTkvLTxjOFvxqeDpPorw==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^6.13.0 || ^7.0.0", + "minimatch": "^9.0.3", + "natural-compare-lite": "^1.4.0" + }, + "peerDependencies": { + "astro-eslint-parser": "^1.0.2", + "eslint": ">=8.0.0", + "svelte": ">=3.0.0", + "svelte-eslint-parser": "^0.37.0", + "vue-eslint-parser": ">=9.0.0" + }, + "peerDependenciesMeta": { + "astro-eslint-parser": { + "optional": true + }, + "svelte": { + "optional": true + }, + "svelte-eslint-parser": { + "optional": true + }, + "vue-eslint-parser": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": ">=6" + "node": "^18.18.0 || >=20.0.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/eslint-plugin-node/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, "engines": { - "node": ">=4" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" } }, - "node_modules/eslint-plugin-node/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "*" + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/eslint-plugin-perfectionist/node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" } }, "node_modules/eslint-plugin-unicorn": { @@ -8155,6 +8537,16 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/eslint/node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -8230,6 +8622,22 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -8260,6 +8668,19 @@ "node": ">=8" } }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -8803,6 +9224,21 @@ } } }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -8896,11 +9332,34 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/fuse.js": { "version": "7.1.0", @@ -9014,6 +9473,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/git-hooks-list": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.2.0.tgz", @@ -9125,30 +9613,32 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", + "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/globby": { @@ -9236,6 +9726,18 @@ "node": ">=0.10.0" } }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9244,6 +9746,33 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -9604,6 +10133,20 @@ "resolved": "https://registry.npmjs.org/inspecjs/-/inspecjs-2.11.0.tgz", "integrity": "sha512-CR6cOXqJ9XAqZz3YoMRMK4s8j6psgRDV5YrlXtKXLpaiRdAfKVoWI5j6TFHj0TK+ykEQzuTr7mBgF9BzJiHBxg==" }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -9612,11 +10155,62 @@ "node": ">= 0.10" } }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -9628,6 +10222,22 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -9648,6 +10258,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-bun-module": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.3.0.tgz", + "integrity": "sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==", + "dev": true, + "dependencies": { + "semver": "^7.6.3" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -9662,6 +10293,39 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -9692,6 +10356,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -9708,6 +10387,24 @@ "node": ">=6" } }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -9750,6 +10447,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -9758,6 +10467,22 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -9785,6 +10510,24 @@ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-retry-allowed": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", @@ -9793,6 +10536,33 @@ "node": ">=0.10.0" } }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -9804,6 +10574,54 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -9821,6 +10639,49 @@ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==" }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-whitespace": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", @@ -12229,21 +13090,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/mocha/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/mocha/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12430,6 +13276,12 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -15108,13 +15960,100 @@ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", "engines": { - "node": ">= 6" + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-treeify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-4.0.1.tgz", + "integrity": "sha512-Y6tg5rHfsefSkfKujv2SwHulInROy/rCL5F4w0QOWxut8AnxYxf0YmNhTh95Zfyxpsudo66uqkux0ACFnyMSgQ==", + "engines": { + "node": ">= 16" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -15122,14 +16061,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-treeify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-4.0.1.tgz", - "integrity": "sha512-Y6tg5rHfsefSkfKujv2SwHulInROy/rCL5F4w0QOWxut8AnxYxf0YmNhTh95Zfyxpsudo66uqkux0ACFnyMSgQ==", - "engines": { - "node": ">= 16" - } - }, "node_modules/objects-to-csv": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/objects-to-csv/-/objects-to-csv-1.3.6.tgz", @@ -15280,110 +16211,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/oclif/node_modules/@inquirer/confirm": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz", - "integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==", - "dev": true, - "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/type": "^1.5.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/oclif/node_modules/@inquirer/core": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", - "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", - "dev": true, - "dependencies": { - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.5.5", - "@types/wrap-ansi": "^3.0.0", - "ansi-escapes": "^4.3.2", - "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", - "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/oclif/node_modules/@inquirer/core/node_modules/@inquirer/type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", - "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", - "dev": true, - "dependencies": { - "mute-stream": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/oclif/node_modules/@inquirer/input": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz", - "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", - "dev": true, - "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/type": "^1.5.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/oclif/node_modules/@inquirer/select": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", - "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", - "dev": true, - "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.3", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/oclif/node_modules/@inquirer/type": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", - "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", - "dev": true, - "dependencies": { - "mute-stream": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/oclif/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/oclif/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -15589,6 +16416,23 @@ "node": ">=0.10.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-cancelable": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", @@ -15925,6 +16769,15 @@ "node": ">=4" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -16113,16 +16966,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/promise-toolbox": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/promise-toolbox/-/promise-toolbox-0.14.0.tgz", @@ -16282,6 +17125,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/rambda": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz", + "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==", + "dev": true + }, "node_modules/ramda": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.2.tgz", @@ -16503,6 +17352,28 @@ "node": ">=8.10.0" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", @@ -16517,6 +17388,26 @@ "regexp-tree": "bin/regexp-tree" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regexpp": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", @@ -16642,6 +17533,15 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/resolve.exports": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", @@ -16771,6 +17671,31 @@ "tslib": "^2.1.0" } }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -16790,6 +17715,28 @@ } ] }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/safe-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", @@ -16799,6 +17746,23 @@ "regexp-tree": "~0.1.1" } }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-stable-stringify": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", @@ -16909,13 +17873,59 @@ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" } }, "node_modules/setimmediate": { @@ -17207,6 +18217,12 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "node_modules/stable-hash": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", + "integrity": "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==", + "dev": true + }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -17311,6 +18327,62 @@ "node": ">=8" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -17396,17 +18468,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strnum": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.0.5.tgz", - "integrity": "sha512-YAT3K/sgpCUxhxNMrrdhtod3jckkpYwH6JAuwmUdXZsmzH1wUyzTMrrK2wYCEEqlKwrWDd35NeuUkbBy/1iK+Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ] - }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -17506,6 +18567,15 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.13.tgz", "integrity": "sha512-gbvFrB0fOsTv/OugXWi2PtflJ4S6/ctu6Mmn3bCftmLY/6xRsQVEJPgIIpABwpZ52DpONkCA3bEj5b54MHxF2Q==" }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -17600,28 +18670,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tiny-jsonc": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tiny-jsonc/-/tiny-jsonc-1.0.2.tgz", - "integrity": "sha512-f5QDAfLq6zIVSyCZQZhhyl0QS6MvAyTxgz4X4x3+EoCktNWEYJ6PeoEA97fyb98njpBNNi88ybpD7m+BDFXaCw==", - "dev": true - }, - "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", - "dev": true, - "dependencies": { - "fdir": "^6.4.3", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, "node_modules/tinyglobby/node_modules/fdir": { "version": "6.4.3", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", @@ -17732,15 +18780,15 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", "dev": true, "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-interface-checker": { @@ -17867,6 +18915,33 @@ "node": ">=0.3.1" } }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/tsimportlib": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/tsimportlib/-/tsimportlib-0.0.5.tgz", @@ -17877,27 +18952,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -18025,6 +19079,80 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", @@ -18037,6 +19165,47 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.26.1.tgz", + "integrity": "sha512-t/oIs9mYyrwZGRpDv3g+3K6nZ5uhKEMt2oNmAPwaY4/ye0+EH4nXIPYNtkYFS6QHm+1DFg34DbglYBz5P9Xysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.26.1", + "@typescript-eslint/parser": "8.26.1", + "@typescript-eslint/utils": "8.26.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", @@ -18140,13 +19309,6 @@ "uuid": "dist/esm/bin/uuid" } }, - "node_modules/v8-compile-cache": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", - "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", - "dev": true, - "peer": true - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -18288,6 +19450,97 @@ "node": "^16.13.0 || >=18.0.0" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/widest-line": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", @@ -18669,19 +19922,6 @@ "node": ">=4.0.0" } }, - "node_modules/yarn": { - "version": "1.22.22", - "resolved": "https://registry.npmjs.org/yarn/-/yarn-1.22.22.tgz", - "integrity": "sha512-prL3kGtyG7o9Z9Sv8IPfBNrWTDmXB4Qbes8A9rEzt6wkJV8mUvoirjU0Mp3GGAU06Y0XQyA3/2/RQFVuK7MTfg==", - "hasInstallScript": true, - "bin": { - "yarn": "bin/yarn.js", - "yarnpkg": "bin/yarn.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/yauzl": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", diff --git a/package.json b/package.json index 0f53aba03..6a8f6b35a 100644 --- a/package.json +++ b/package.json @@ -84,22 +84,27 @@ }, "devDependencies": { "@e965/xlsx": "^0.20.0", + "@eslint/js": "^9.22.0", "@oclif/test": "^4.1.0", "@types/js-yaml": "^4.0.9", "@types/jsdom": "^21.1.7", "@types/mock-fs": "^4.13.4", - "@typescript-eslint/eslint-plugin": "~7.18.0", - "eslint": "^8.48.0", + "@typescript-eslint/eslint-plugin": "^8.26.1", + "@typescript-eslint/parser": "^8.26.1", + "eslint": "^8.57.1", "eslint-config-oclif": "^4.0", - "eslint-config-oclif-typescript": "^1.0.3", - "eslint-plugin-unicorn": "^56.0.0", + "eslint-config-oclif-typescript": "^3.1.14", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-unicorn": "^56.0.1", + "globals": "^16.0.0", "jsdom": "^26.0.0", "marked": "^15.0.0", "mocha": "^11", "mock-fs": "^5.2.0", "oclif": "^4.8.8", "tmp": "^0.2.1", - "ts-mocha": "^11.1.0" + "ts-mocha": "^11.1.0", + "typescript-eslint": "^8.26.1" }, "engines": { "node": "^22.0.0" @@ -201,7 +206,8 @@ "repository": "mitre/saf", "scripts": { "lint": "eslint \"src/**/*.ts\" --fix", - "lint:ci": "eslint \"src/**/*.ts\" --max-warnings 0", + "lint:ci": "eslint \"src/**/*.ts\" --max-warnings 0 --debug", + "lint:fix": "eslint \"src/**/*.ts\" --fix", "dev": "run-script-os", "dev:win32": "(IF EXIST lib rmdir /s /q lib) && tsc && node bin/run", "dev:darwin:linux": "rm -rf lib && tsc && node bin/run", diff --git a/src/commands/attest/apply.ts b/src/commands/attest/apply.ts index 0d63913cc..e65210fb5 100644 --- a/src/commands/attest/apply.ts +++ b/src/commands/attest/apply.ts @@ -9,73 +9,73 @@ import {convertFullPathToFilename} from '../../utils/global' import {BaseCommand} from '../../utils/oclif/baseCommand' export default class ApplyAttestation extends BaseCommand { - static readonly usage = '<%= command.id %> -i ... ... -o ' + static readonly usage = '<%= command.id %> -i ... ... -o ' - static readonly description = 'Apply one or more attestation files to one or more HDF results sets' + static readonly description = 'Apply one or more attestation files to one or more HDF results sets' - static readonly examples = [ - '<%= config.bin %> <%= command.id %> -i hdf.json attestation.json -o new-hdf.json', - '<%= config.bin %> <%= command.id %> -i hdf1.json hdf2.json attestation.xlsx -o outputDir', - ] + static readonly examples = [ + '<%= config.bin %> <%= command.id %> -i hdf.json attestation.json -o new-hdf.json', + '<%= config.bin %> <%= command.id %> -i hdf1.json hdf2.json attestation.xlsx -o outputDir', + ] - static readonly flags = { - input: Flags.string({char: 'i', required: true, multiple: true, description: 'Your input HDF and Attestation file(s)'}), - output: Flags.string({char: 'o', required: true, description: 'Output file or folder (for multiple executions)'}), - } + static readonly flags = { + input: Flags.string({char: 'i', required: true, multiple: true, description: 'Your input HDF and Attestation file(s)'}), + output: Flags.string({char: 'o', required: true, description: 'Output file or folder (for multiple executions)'}), + } - async run() { - const {flags} = await this.parse(ApplyAttestation) + async run() { + const {flags} = await this.parse(ApplyAttestation) - const attestations: Attestation[] = [] - const executions: Record = {} + const attestations: Attestation[] = [] + const executions: Record = {} - for (const inputFile of flags.input) { - let inputData - try { - inputData = JSON.parse(fs.readFileSync(inputFile, 'utf8')) - if (Array.isArray(inputData) && inputData.length > 0 && _.get(inputData, '[0].control_id')) { - // We have an attestations JSON - attestations.push(...inputData) - } else if (Array.isArray(_.get(inputData, 'plugins.inspec-reporter-json-hdf.attestations'))) { - // We have a legacy Inspec Tools attestations file - attestations.push(..._.get(inputData, 'plugins.inspec-reporter-json-hdf.attestations')) - } else if ('profiles' in inputData) { - // We have an execution file - executions[convertFullPathToFilename(inputFile)] = inputData - } else { - // Unknown file - console.error(`Unknown input file: ${inputFile}`) - process.exit(1) - } - } catch { - inputData = fs.readFileSync(inputFile, 'utf8') - if (inputFile.toLowerCase().endsWith('xlsx')) { - // We have a spreadsheet - attestations.push(...(await parseXLSXAttestations(fs.readFileSync(inputFile, null)))) - } else if (inputFile.toLowerCase().endsWith('yml') || inputFile.toLowerCase().endsWith('yaml')) { - // We have a YAML - attestations.push(...yaml.parse(inputData)) - } else { - throw new Error(`Unknown input file: ${inputFile}`) - } + for (const inputFile of flags.input) { + let inputData + try { + inputData = JSON.parse(fs.readFileSync(inputFile, 'utf8')) + if (Array.isArray(inputData) && inputData.length > 0 && _.get(inputData, '[0].control_id')) { + // We have an attestations JSON + attestations.push(...inputData) + } else if (Array.isArray(_.get(inputData, 'plugins.inspec-reporter-json-hdf.attestations'))) { + // We have a legacy Inspec Tools attestations file + attestations.push(..._.get(inputData, 'plugins.inspec-reporter-json-hdf.attestations')) + } else if ('profiles' in inputData) { + // We have an execution file + executions[convertFullPathToFilename(inputFile)] = inputData + } else { + // Unknown file + console.error(`Unknown input file: ${inputFile}`) + process.exit(1) + } + } catch { + inputData = fs.readFileSync(inputFile, 'utf8') + if (inputFile.toLowerCase().endsWith('xlsx')) { + // We have a spreadsheet + attestations.push(...(await parseXLSXAttestations(fs.readFileSync(inputFile, null)))) + } else if (inputFile.toLowerCase().endsWith('yml') || inputFile.toLowerCase().endsWith('yaml')) { + // We have a YAML + attestations.push(...yaml.parse(inputData)) + } else { + throw new Error(`Unknown input file: ${inputFile}`) } } + } - if (Object.entries(executions).length > 1 && !fs.existsSync(flags.output)) { - fs.mkdirSync(flags.output) - } + if (Object.entries(executions).length > 1 && !fs.existsSync(flags.output)) { + fs.mkdirSync(flags.output) + } - if (Object.keys(executions).length === 0) { - throw new Error('Please provide at least one HDF file') - } + if (Object.keys(executions).length === 0) { + throw new Error('Please provide at least one HDF file') + } - for (const [originalFilename, execution] of Object.entries(executions)) { - const applied = addAttestationToHDF(execution, attestations) - if (Object.entries(executions).length <= 1) { - fs.writeFileSync(flags.output, JSON.stringify(applied, null, 2)) - } else { - fs.writeFileSync(path.join(flags.output, originalFilename), JSON.stringify(applied, null, 2)) - } + for (const [originalFilename, execution] of Object.entries(executions)) { + const applied = addAttestationToHDF(execution, attestations) + if (Object.entries(executions).length <= 1) { + fs.writeFileSync(flags.output, JSON.stringify(applied, null, 2)) + } else { + fs.writeFileSync(path.join(flags.output, originalFilename), JSON.stringify(applied, null, 2)) } } + } } diff --git a/src/commands/attest/create.ts b/src/commands/attest/create.ts index 6fdaf955c..83abeac7b 100644 --- a/src/commands/attest/create.ts +++ b/src/commands/attest/create.ts @@ -14,131 +14,131 @@ const MAX_SEARCH_RESULTS = 5 const prompt = promptSync() export default class CreateAttestations extends BaseCommand { - static readonly usage = '<%= command.id %> -o [-i -t ]' + static readonly usage = '<%= command.id %> -o [-i -t ]' + + static readonly description = 'Create attestation files for use with `saf attest apply`' + + static readonly examples = [ + '<%= config.bin %> <%= command.id %> -o attestation.json -i hdf.json', + '<%= config.bin %> <%= command.id %> -o attestation.xlsx -t xlsx', + ] + + static readonly flags = { + input: Flags.string({char: 'i', description: '(optional) An input HDF file to search for controls'}), + output: Flags.string({char: 'o', required: true, description: 'The output filename'}), + format: Flags.string({char: 't', description: '(optional) The output file type', default: 'json', options: ['json', 'xlsx', 'yml', 'yaml']}), + } + + promptForever(promptValue: string): string { // skipcq: JS-0105 + while (true) { + const ret = prompt(promptValue) + if (ret.trim() !== '') { + return ret + } + } + } + + getStatus(): 'passed' | 'failed' { // skipcq: JS-0105 + const validPassResponses = new Set(['p', 'passed', 'pass']) + const validFailResponses = new Set(['f', 'failed', 'fail', 'failure']) + while (true) { + const input = prompt('Enter status ((p)assed/(f)ailed): ') || '' + if (validPassResponses.has(input.trim().toLowerCase())) { + return 'passed' + } - static readonly description = 'Create attestation files for use with `saf attest apply`' + if (validFailResponses.has(input.trim().toLowerCase())) { + return 'failed' + } + } + } + + promptForAttestation(id: string): Attestation { + return { + control_id: id, + explanation: this.promptForever('Attestation explanation: '), + frequency: this.promptForever('Attestation valid for (enter a number followed by d/w/m/y to indicate days, weeks, months, or years, e.g. 1d/6w/3m/1y/1.5y/custom):'), + status: this.getStatus(), + updated: new Date().toISOString(), + updated_by: this.promptForever('Updated By: '), + } + } - static readonly examples = [ - '<%= config.bin %> <%= command.id %> -o attestation.json -i hdf.json', - '<%= config.bin %> <%= command.id %> -o attestation.xlsx -t xlsx', - ] + async run() { + const {flags} = await this.parse(CreateAttestations) - static readonly flags = { - input: Flags.string({char: 'i', description: '(optional) An input HDF file to search for controls'}), - output: Flags.string({char: 'o', required: true, description: 'The output filename'}), - format: Flags.string({char: 't', description: '(optional) The output file type', default: 'json', options: ['json', 'xlsx', 'yml', 'yaml']}), - } + const attestations: Attestation[] = [] - promptForever(promptValue: string): string { // skipcq: JS-0105 - while (true) { - const ret = prompt(promptValue) - if (ret.trim() !== '') { - return ret + if (flags.input) { + const evaluation = JSON.parse(fs.readFileSync(flags.input, 'utf8')) as ExecJSON.Execution + const search = new AccurateSearch() + const controls: Record = {} + for (const profile of evaluation.profiles) { + for (const control of profile.controls) { + controls[control.id] = control + search.addText(control.id, `${control.id}: ${control.title || ''} ${control.desc || ''}`) } } - } - getStatus(): 'passed' | 'failed' { // skipcq: JS-0105 - const validPassResponses = new Set(['p', 'passed', 'pass']) - const validFailResponses = new Set(['f', 'failed', 'fail', 'failure']) while (true) { - const input = prompt('Enter status ((p)assed/(f)ailed): ') || '' - if (validPassResponses.has(input.trim().toLowerCase())) { - return 'passed' + const input = prompt("Enter a control ID, search for a control, or enter 'q' to exit: ") + if (input.trim().toLowerCase() === 'q') { + break + } else if (input in controls) { + attestations.push(this.promptForAttestation(controls[input].id)) + } else { + const ids = search.search(input).slice(0, MAX_SEARCH_RESULTS) + for (const id of ids) { + const control = controls[id] + console.log(`\t${control.id}: ${control.title?.replaceAll('\n', '').replaceAll(/\s\s+/g, ' ')}`) + } } - - if (validFailResponses.has(input.trim().toLowerCase())) { - return 'failed' + } + } else { + while (true) { + const input = prompt("Enter a control ID or enter 'q' to exit: ") + if (input.trim().toLowerCase() === 'q') { + break + } else { + attestations.push(this.promptForAttestation(input)) } } } - promptForAttestation(id: string): Attestation { - return { - control_id: id, - explanation: this.promptForever('Attestation explanation: '), - frequency: this.promptForever('Attestation valid for (enter a number followed by d/w/m/y to indicate days, weeks, months, or years, e.g. 1d/6w/3m/1y/1.5y/custom):'), - status: this.getStatus(), - updated: new Date().toISOString(), - updated_by: this.promptForever('Updated By: '), + switch (flags.format) { + case 'json': { + fs.writeFileSync(flags.output, JSON.stringify(attestations, null, 2)) + break } - } - - async run() { - const {flags} = await this.parse(CreateAttestations) - const attestations: Attestation[] = [] - - if (flags.input) { - const evaluation = JSON.parse(fs.readFileSync(flags.input, 'utf8')) as ExecJSON.Execution - const search = new AccurateSearch() - const controls: Record = {} - for (const profile of evaluation.profiles) { - for (const control of profile.controls) { - controls[control.id] = control - search.addText(control.id, `${control.id}: ${control.title || ''} ${control.desc || ''}`) + case 'xlsx': { + XlsxPopulate.fromDataAsync(dataURLtoU8Array(files.AttestationTemplate.data)).then((workBook: any) => { + const sheet = workBook.sheet(0) // Attestations worksheet + let currentRow = 2 + for (const attestation of attestations) { + sheet.cell(`A${currentRow}`).value(attestation.control_id) + sheet.cell(`B${currentRow}`).value(attestation.explanation) + sheet.cell(`C${currentRow}`).value(attestation.frequency) + sheet.cell(`D${currentRow}`).value(attestation.status) + sheet.cell(`E${currentRow}`).value(attestation.updated) + sheet.cell(`F${currentRow}`).value(attestation.updated_by) + currentRow++ } - } - while (true) { - const input = prompt("Enter a control ID, search for a control, or enter 'q' to exit: ") - if (input.trim().toLowerCase() === 'q') { - break - } else if (input in controls) { - attestations.push(this.promptForAttestation(controls[input].id)) - } else { - const ids = search.search(input).slice(0, MAX_SEARCH_RESULTS) - for (const id of ids) { - const control = controls[id] - console.log(`\t${control.id}: ${control.title?.replaceAll('\n', '').replaceAll(/\s\s+/g, ' ')}`) - } - } - } - } else { - while (true) { - const input = prompt("Enter a control ID or enter 'q' to exit: ") - if (input.trim().toLowerCase() === 'q') { - break - } else { - attestations.push(this.promptForAttestation(input)) - } - } + return workBook.toFileAsync(flags.output) + }) + break } - switch (flags.format) { - case 'json': { - fs.writeFileSync(flags.output, JSON.stringify(attestations, null, 2)) - break - } - - case 'xlsx': { - XlsxPopulate.fromDataAsync(dataURLtoU8Array(files.AttestationTemplate.data)).then((workBook: any) => { - const sheet = workBook.sheet(0) // Attestations worksheet - let currentRow = 2 - for (const attestation of attestations) { - sheet.cell(`A${currentRow}`).value(attestation.control_id) - sheet.cell(`B${currentRow}`).value(attestation.explanation) - sheet.cell(`C${currentRow}`).value(attestation.frequency) - sheet.cell(`D${currentRow}`).value(attestation.status) - sheet.cell(`E${currentRow}`).value(attestation.updated) - sheet.cell(`F${currentRow}`).value(attestation.updated_by) - currentRow++ - } - - return workBook.toFileAsync(flags.output) - }) - break - } - - case 'yaml': - case 'yml': { - fs.writeFileSync(flags.output, yaml.stringify(attestations)) - break - } + case 'yaml': + case 'yml': { + fs.writeFileSync(flags.output, yaml.stringify(attestations)) + break + } - default: { - throw new Error('Invalid file output type') - } + default: { + throw new Error('Invalid file output type') } } + } } diff --git a/src/commands/convert/asff2hdf.ts b/src/commands/convert/asff2hdf.ts index be69488ff..c2d91ab91 100644 --- a/src/commands/convert/asff2hdf.ts +++ b/src/commands/convert/asff2hdf.ts @@ -23,7 +23,7 @@ const API_MAX_RESULTS = 100 export default class ASFF2HDF extends BaseCommand { static readonly usage = - '<%= command.id %> -o [--interactive] [-L info|warn|debug|verbose]' + + '<%= command.id %> -o [--interactive] [-L info|warn|debug|verbose]' + ' [-i | -a | -r | -I | -C | -t ...] [-H ...]' static readonly description = diff --git a/src/commands/convert/ckl2POAM.ts b/src/commands/convert/ckl2POAM.ts index 409985905..52768f35b 100644 --- a/src/commands/convert/ckl2POAM.ts +++ b/src/commands/convert/ckl2POAM.ts @@ -1,7 +1,6 @@ import {Flags} from '@oclif/core' import fs from 'fs' import path from 'path' -import _ from 'lodash' import {createLogger, format, transports} from 'winston' import xml2js from 'xml2js' import {STIG, Vulnerability, STIGHolder} from '../../types/STIG' diff --git a/src/commands/convert/hdf2csv.ts b/src/commands/convert/hdf2csv.ts index aa51707e7..13d34f58f 100644 --- a/src/commands/convert/hdf2csv.ts +++ b/src/commands/convert/hdf2csv.ts @@ -18,7 +18,6 @@ import { import {EventEmitter} from 'events' // eslint-disable-next-line no-restricted-imports import colors from 'colors' -// eslint-disable-next-line node/no-extraneous-import import {checkbox, input, select} from '@inquirer/prompts' export default class HDF2CSV extends BaseCommand { @@ -26,21 +25,21 @@ export default class HDF2CSV extends BaseCommand { '<%= command.id %> [-i |--interactive] [-o |--interactive] ' + ' [-f |--interactive] [-t|--interactive] [-L info|warn|debug|verbose]' - static readonly description = 'Translate a Heimdall Data Format JSON file into a Comma Separated Values (CSV) file' + static readonly description = 'Translate a Heimdall Data Format JSON file into a Comma Separated Values (CSV) file' - static readonly examples = [ - { - description: '\x1B[93mRunning the CLI interactively\x1B[0m', - command: '<%= config.bin %> <%= command.id %> --interactive', - }, - { - description: '\x1B[93mProviding flags at the command line\x1B[0m', - command: '<%= config.bin %> <%= command.id %> -i rhel7-results.json -o rhel7.csv --fields "Results Set,Status,ID,Title,Severity"', - }, - ] + static readonly examples = [ + { + description: '\x1B[93mRunning the CLI interactively\x1B[0m', + command: '<%= config.bin %> <%= command.id %> --interactive', + }, + { + description: '\x1B[93mProviding flags at the command line\x1B[0m', + command: '<%= config.bin %> <%= command.id %> -i rhel7-results.json -o rhel7.csv --fields "Results Set,Status,ID,Title,Severity"', + }, + ] - // eslint-disable-next-line no-warning-comments - /* + // eslint-disable-next-line no-warning-comments + /* TODO: Find a way to make certain flags required when not using --interactive. In this CLI the -i and -o are required fields, but if required: is set to true, when using the --interactive the process will state that those @@ -49,125 +48,125 @@ export default class HDF2CSV extends BaseCommand { The exclusive: ['interactive'] flag option is used to state that the flag cannot be specified alongside the --interactive flag */ - static readonly flags = { - input: Flags.string({ - char: 'i', - required: false, - exclusive: ['interactive'], - description: '\x1B[31m(required if not --interactive)\x1B[34m Input HDF file'}), - output: Flags.string({ - char: 'o', - required: false, - exclusive: ['interactive'], - description: '\x1B[31m(required if not --interactive)\x1B[34m Output CSV file'}), - fields: Flags.string({ - char: 'f', - required: false, - exclusive: ['interactive'], - default: csvExportFields.join(','), - description: 'Fields to include in output CSV, separated by commas', - }), - noTruncate: Flags.boolean({ - char: 't', - required: false, - exclusive: ['interactive'], - default: false, - description: 'Do not truncate fields longer than 32,767 characters (the cell limit in Excel)'}), - } + static readonly flags = { + input: Flags.string({ + char: 'i', + required: false, + exclusive: ['interactive'], + description: '\x1B[31m(required if not --interactive)\x1B[34m Input HDF file'}), + output: Flags.string({ + char: 'o', + required: false, + exclusive: ['interactive'], + description: '\x1B[31m(required if not --interactive)\x1B[34m Output CSV file'}), + fields: Flags.string({ + char: 'f', + required: false, + exclusive: ['interactive'], + default: csvExportFields.join(','), + description: 'Fields to include in output CSV, separated by commas', + }), + noTruncate: Flags.boolean({ + char: 't', + required: false, + exclusive: ['interactive'], + default: false, + description: 'Do not truncate fields longer than 32,767 characters (the cell limit in Excel)'}), + } - async run() { - const {flags} = await this.parse(HDF2CSV) + async run() { + const {flags} = await this.parse(HDF2CSV) - addToProcessLogData('================== HDF2CSV CLI Process ===================') - addToProcessLogData(`Date: ${new Date().toISOString()}\n`) + addToProcessLogData('================== HDF2CSV CLI Process ===================') + addToProcessLogData(`Date: ${new Date().toISOString()}\n`) - let inputFile = '' - let outputFile = '' - let includeFields = '' - let truncateFields = false + let inputFile = '' + let outputFile = '' + let includeFields = '' + let truncateFields = false - if (flags.interactive) { - const interactiveFlags = await getFlags() - inputFile = interactiveFlags.inputFile - outputFile = path.join(interactiveFlags.outputDirectory, interactiveFlags.outputFileName) - includeFields = interactiveFlags.fields.join(',') - truncateFields = Boolean(interactiveFlags.truncateFields) - } else if (this.requiredFlagsProvided(flags)) { - inputFile = flags.input as string - outputFile = flags.output as string - includeFields = flags.fields - truncateFields = flags.noTruncate + if (flags.interactive) { + const interactiveFlags = await getFlags() + inputFile = interactiveFlags.inputFile + outputFile = path.join(interactiveFlags.outputDirectory, interactiveFlags.outputFileName) + includeFields = interactiveFlags.fields.join(',') + truncateFields = Boolean(interactiveFlags.truncateFields) + } else if (this.requiredFlagsProvided(flags)) { + inputFile = flags.input as string + outputFile = flags.output as string + includeFields = flags.fields + truncateFields = flags.noTruncate - // Save the flags to the log object - addToProcessLogData('Process Flags ============================================') - for (const key in flags) { - if (Object.prototype.hasOwnProperty.call(flags, key)) { - addToProcessLogData(key + '=' + flags[key as keyof typeof flags]) - } + // Save the flags to the log object + addToProcessLogData('Process Flags ============================================') + for (const key in flags) { + if (Object.prototype.hasOwnProperty.call(flags, key)) { + addToProcessLogData(key + '=' + flags[key as keyof typeof flags]) } - } else { - return } + } else { + return + } - if (validFileFlags(inputFile, outputFile)) { - const contextualizedEvaluation = contextualizeEvaluation(JSON.parse(fs.readFileSync(inputFile, 'utf8'))) - - // Convert all controls from a file to ControlSetRows - let rows: ControlSetRows = convertRows(contextualizedEvaluation, convertFullPathToFilename(inputFile), includeFields.split(',')) - rows = rows.map((row, index) => { - const cleanedRow: Record = {} - for (const key in row) { - if (row[key] !== undefined) { - if ((row[key]).length > 32767 && truncateFields) { - if ('ID' in row) { - console.error(`Field ${key} of control ${row.ID} is longer than 32,767 characters and has been truncated for compatibility with Excel. To disable this behavior use the option --noTruncate`) - } else { - console.error(`Field ${key} of control at index ${index} is longer than 32,767 characters and has been truncated for compatibility with Excel. To disable this behavior use the option --noTruncate`) - } + if (validFileFlags(inputFile, outputFile)) { + const contextualizedEvaluation = contextualizeEvaluation(JSON.parse(fs.readFileSync(inputFile, 'utf8'))) - cleanedRow[key] = _.truncate(row[key], {length: 32757, omission: 'TRUNCATED'}) + // Convert all controls from a file to ControlSetRows + let rows: ControlSetRows = convertRows(contextualizedEvaluation, convertFullPathToFilename(inputFile), includeFields.split(',')) + rows = rows.map((row, index) => { + const cleanedRow: Record = {} + for (const key in row) { + if (row[key] !== undefined) { + if ((row[key]).length > 32767 && truncateFields) { + if ('ID' in row) { + console.error(`Field ${key} of control ${row.ID} is longer than 32,767 characters and has been truncated for compatibility with Excel. To disable this behavior use the option --noTruncate`) } else { - cleanedRow[key] = row[key] + console.error(`Field ${key} of control at index ${index} is longer than 32,767 characters and has been truncated for compatibility with Excel. To disable this behavior use the option --noTruncate`) } + + cleanedRow[key] = _.truncate(row[key], {length: 32757, omission: 'TRUNCATED'}) + } else { + cleanedRow[key] = row[key] } } + } - return cleanedRow - }) + return cleanedRow + }) - try { - await new ObjectsToCsv(rows).toDisk(outputFile) - printGreen('\nTranslation completed successfully\n') - } catch (error: any) { - const error_ = error.code === 'EISDIR' ? new Error('The CSV output file mane was not provided.') : error - printRed(`\nTranslation failed: ${error_}\n`) - } finally { - saveProcessLogData() - } + try { + await new ObjectsToCsv(rows).toDisk(outputFile) + printGreen('\nTranslation completed successfully\n') + } catch (error: any) { + const error_ = error.code === 'EISDIR' ? new Error('The CSV output file mane was not provided.') : error + printRed(`\nTranslation failed: ${error_}\n`) + } finally { + saveProcessLogData() } } + } - requiredFlagsProvided(flags: { input: any; output: any }): boolean { - let missingFlags = false - let strMsg = 'Warning: The following errors occurred:\n' + requiredFlagsProvided(flags: { input: any; output: any }): boolean { + let missingFlags = false + let strMsg = 'Warning: The following errors occurred:\n' - if (!flags.input) { - strMsg += colors.dim(' Missing required flag input (HDF file)\n') - missingFlags = true - } - - if (!flags.output) { - strMsg += colors.dim(' Missing required flag output (CSV file)\n') - missingFlags = true - } + if (!flags.input) { + strMsg += colors.dim(' Missing required flag input (HDF file)\n') + missingFlags = true + } - if (missingFlags) { - strMsg += 'See more help with -h or --help' - this.warn(strMsg) - } + if (!flags.output) { + strMsg += colors.dim(' Missing required flag output (CSV file)\n') + missingFlags = true + } - return !missingFlags + if (missingFlags) { + strMsg += 'See more help with -h or --help' + this.warn(strMsg) } + + return !missingFlags + } } function convertRows(evaluation: ContextualizedEvaluation, filename: string, fieldsToAdd: string[]): ControlSetRows { diff --git a/src/commands/emasser/configure.ts b/src/commands/emasser/configure.ts index 757098a4e..e1e19e565 100644 --- a/src/commands/emasser/configure.ts +++ b/src/commands/emasser/configure.ts @@ -10,7 +10,7 @@ export default class EmasserBuildConfig extends Command { 'most eMASS integration, however certain integrations, the user-uid is not required' static readonly description = - ` + ` ${colors.yellow('Required eMASS configuration variables 👇')} ${colors.blue('\tEMASSER_API_KEY') + colors.green(' 30 alpha numeric characters>\b')} ${colors.blue('\tEMASSER_HOST_URL') + colors.green(' ')} diff --git a/src/commands/emasser/get/controls.ts b/src/commands/emasser/get/controls.ts index 724759427..224d3d21b 100644 --- a/src/commands/emasser/get/controls.ts +++ b/src/commands/emasser/get/controls.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import {colorize} from 'json-colorizer' import {Command, Flags} from '@oclif/core' import {ApiConnection} from '../../../utils/emasser/apiConnection' diff --git a/src/commands/emasser/get/dashboards.ts b/src/commands/emasser/get/dashboards.ts index 2dd4bc537..359f98746 100644 --- a/src/commands/emasser/get/dashboards.ts +++ b/src/commands/emasser/get/dashboards.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import {colorize} from 'json-colorizer' import {Args, Command, Flags} from '@oclif/core' import {ApiConnection} from '../../../utils/emasser/apiConnection' @@ -158,7 +159,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemStatusDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -168,7 +169,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemTermsConditionsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -178,7 +179,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemTermsConditionsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -188,7 +189,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemConnectivityCcsdDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -198,7 +199,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemConnectivityCcsdSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -208,7 +209,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemAtcIatcDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -218,7 +219,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemQuestionnaireSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -228,7 +229,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemQuestionnaireDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -238,7 +239,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemWorkflowsHistorySummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -248,7 +249,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemWorkflowsHistoryDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -258,7 +259,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemWorkflowsHistoryStageDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -268,7 +269,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemControlComplianceSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -278,7 +279,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemSecurityControlDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -288,7 +289,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemAssessmentProceduresDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -298,7 +299,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemPoamSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -308,7 +309,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemPoamDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -318,7 +319,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemArtifactsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -328,7 +329,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemArtifactsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -338,7 +339,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemHardwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -348,7 +349,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemHardwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -358,7 +359,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemSensorHardwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -368,7 +369,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemSensorHardwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -378,7 +379,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemSoftwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -388,7 +389,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemSoftwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -398,7 +399,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemSensorSoftwareSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -408,7 +409,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemSensorSoftwareDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -418,7 +419,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemSensorSoftwareCounts(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -428,7 +429,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemCriticalAssetsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -438,7 +439,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemVulnerabilitySummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -448,7 +449,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemDeviceFindingsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -458,7 +459,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemDeviceFindingsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -468,7 +469,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemApplicationFindingsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -478,7 +479,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemApplicationFindingsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -488,7 +489,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemPortsProtocolsSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -498,7 +499,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemPortsProtocolsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -508,7 +509,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemCommonIntegrationStatusSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -518,7 +519,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemAssociationsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -528,7 +529,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getUserSystemAssignmentsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -538,7 +539,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getOrganizationMigrationStatusSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -548,7 +549,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemMigrationStatusSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -558,7 +559,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemFismaMetrics(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -568,7 +569,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getCoastGuardSystemFismaMetrics(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -578,7 +579,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getSystemPrivacySummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -588,7 +589,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getVaOmbFsmaSaopSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -598,7 +599,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getVaSystemIcampTableauPoamDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -608,7 +609,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getVaSystemAaSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -618,7 +619,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getVaSystemA2Summary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -628,7 +629,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getVaSystemPl109ReportingSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -638,7 +639,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getVaSystemFismaInvetorySummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -648,7 +649,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getVaSystemFismaInvetoryCryptoSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -658,7 +659,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getVaSystemThreatRiskSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -668,7 +669,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getVaSystemThreatSourceDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -678,7 +679,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getVaSystemThreatArchitectureDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -688,7 +689,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getCmmcAssessmentStatusSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -698,7 +699,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getCmmcAssessmentRequirementsComplianceSummary(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -708,7 +709,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getCmmcAssessmentSecurityRequirementsDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } @@ -718,7 +719,7 @@ export default class EmasserGetDashboards extends Command { // Order is important here getDashboard.getCmmcAssessmentRequirementObjectivesDetails(flags.orgId, flags.excludeInherited, flags.pageIndex, flags.pageSize).then((response: object) => { console.log(colorize(outputFormat(response))) - }).catch((error:any) => console.error(colorize(outputError(error)))) + }).catch((error: any) => console.error(colorize(outputError(error)))) break } diff --git a/src/commands/supplement/passthrough/read.ts b/src/commands/supplement/passthrough/read.ts index 0b6a6d5d5..e532d4e09 100644 --- a/src/commands/supplement/passthrough/read.ts +++ b/src/commands/supplement/passthrough/read.ts @@ -4,28 +4,28 @@ import fs from 'fs' import {BaseCommand} from '../../../utils/oclif/baseCommand' export default class ReadPassthrough extends BaseCommand { - static readonly usage = '<%= command.id %> -i [-o ]' + static readonly usage = '<%= command.id %> -i [-o ]' - static readonly description = 'Read the `passthrough` attribute in a given Heimdall Data Format JSON file and send it to stdout or write it to a file' + static readonly description = 'Read the `passthrough` attribute in a given Heimdall Data Format JSON file and send it to stdout or write it to a file' - static readonly examples = ['<%= config.bin %> <%= command.id %> -i hdf.json -o passthrough.json'] + static readonly examples = ['<%= config.bin %> <%= command.id %> -i hdf.json -o passthrough.json'] - static readonly flags = { - input: Flags.string({char: 'i', required: true, description: 'An input HDF file'}), - output: Flags.string({char: 'o', description: 'An output `passthrough` JSON file (otherwise the data is sent to stdout)'}), - } + static readonly flags = { + input: Flags.string({char: 'i', required: true, description: 'An input HDF file'}), + output: Flags.string({char: 'o', description: 'An output `passthrough` JSON file (otherwise the data is sent to stdout)'}), + } - async run() { - const {flags} = await this.parse(ReadPassthrough) + async run() { + const {flags} = await this.parse(ReadPassthrough) - const input: ExecJSON.Execution & {passthrough?: unknown} = JSON.parse(fs.readFileSync(flags.input, 'utf8')) + const input: ExecJSON.Execution & {passthrough?: unknown} = JSON.parse(fs.readFileSync(flags.input, 'utf8')) - const passthrough = input.passthrough || {} + const passthrough = input.passthrough || {} - if (flags.output) { - fs.writeFileSync(flags.output, JSON.stringify(passthrough, null, 2)) - } else { - process.stdout.write(JSON.stringify(passthrough, null, 2)) - } + if (flags.output) { + fs.writeFileSync(flags.output, JSON.stringify(passthrough, null, 2)) + } else { + process.stdout.write(JSON.stringify(passthrough, null, 2)) } + } } diff --git a/src/commands/supplement/passthrough/write.ts b/src/commands/supplement/passthrough/write.ts index 36c9ec3d3..c8c47122f 100644 --- a/src/commands/supplement/passthrough/write.ts +++ b/src/commands/supplement/passthrough/write.ts @@ -4,55 +4,55 @@ import fs from 'fs' import {BaseCommand} from '../../../utils/oclif/baseCommand' export default class WritePassthrough extends BaseCommand { - static readonly usage = '<%= command.id %> -i (-f | -d ) [-o ]' - - static readonly summary = 'Overwrite the `passthrough` attribute in a given HDF file with the provided `passthrough` JSON data' - - static readonly description = 'Passthrough data can be any context/structure. See sample ideas at https://github.com/mitre/saf/wiki/Supplement-HDF-files-with-additional-information-(ex.-%60passthrough%60,-%60target%60)' - - static readonly examples = [ - { - description: '\x1B[93mProviding passthrough-data\x1B[0m', - command: '<%= config.bin %> <%= command.id %> -i hdf.json -d \'{"a": 5}\'', - }, - { - description: '\x1B[93mUsing passthrough-data file\x1B[0m', - command: '<%= config.bin %> <%= command.id %> -i hdf.json -f passthrough.json -o new-hdf.json', - }, - ] - - static readonly flags = { - input: Flags.string({char: 'i', required: true, description: 'An input Heimdall Data Format file'}), - passthroughFile: Flags.string({char: 'f', exclusive: ['passthroughData'], description: 'An input passthrough-data file (can contain any valid JSON); this flag or `passthroughData` must be provided'}), - passthroughData: Flags.string({char: 'd', exclusive: ['passthroughFile'], description: 'Input passthrough-data (can be any valid JSON); this flag or `passthroughFile` must be provided'}), - output: Flags.string({char: 'o', description: 'An output Heimdall Data Format JSON file (otherwise the input file is overwritten)'}), - } - - async run() { - const {flags} = await this.parse(WritePassthrough) - - const input: ExecJSON.Execution & {passthrough?: unknown} = JSON.parse(fs.readFileSync(flags.input, 'utf8')) - const output: string = flags.output || flags.input - - let passthrough: unknown - if (flags.passthroughFile) { - try { - passthrough = JSON.parse(fs.readFileSync(flags.passthroughFile, 'utf8')) - } catch (error: unknown) { - throw new Error(`Couldn't parse passthrough data: ${error}`) - } - } else if (flags.passthroughData) { - try { - passthrough = JSON.parse(flags.passthroughData) - } catch { - passthrough = flags.passthroughData - } - } else { - throw new Error('One out of passthroughFile or passthroughData must be passed') + static readonly usage = '<%= command.id %> -i (-f | -d ) [-o ]' + + static readonly summary = 'Overwrite the `passthrough` attribute in a given HDF file with the provided `passthrough` JSON data' + + static readonly description = 'Passthrough data can be any context/structure. See sample ideas at https://github.com/mitre/saf/wiki/Supplement-HDF-files-with-additional-information-(ex.-%60passthrough%60,-%60target%60)' + + static readonly examples = [ + { + description: '\x1B[93mProviding passthrough-data\x1B[0m', + command: '<%= config.bin %> <%= command.id %> -i hdf.json -d \'{"a": 5}\'', + }, + { + description: '\x1B[93mUsing passthrough-data file\x1B[0m', + command: '<%= config.bin %> <%= command.id %> -i hdf.json -f passthrough.json -o new-hdf.json', + }, + ] + + static readonly flags = { + input: Flags.string({char: 'i', required: true, description: 'An input Heimdall Data Format file'}), + passthroughFile: Flags.string({char: 'f', exclusive: ['passthroughData'], description: 'An input passthrough-data file (can contain any valid JSON); this flag or `passthroughData` must be provided'}), + passthroughData: Flags.string({char: 'd', exclusive: ['passthroughFile'], description: 'Input passthrough-data (can be any valid JSON); this flag or `passthroughFile` must be provided'}), + output: Flags.string({char: 'o', description: 'An output Heimdall Data Format JSON file (otherwise the input file is overwritten)'}), + } + + async run() { + const {flags} = await this.parse(WritePassthrough) + + const input: ExecJSON.Execution & {passthrough?: unknown} = JSON.parse(fs.readFileSync(flags.input, 'utf8')) + const output: string = flags.output || flags.input + + let passthrough: unknown + if (flags.passthroughFile) { + try { + passthrough = JSON.parse(fs.readFileSync(flags.passthroughFile, 'utf8')) + } catch (error: unknown) { + throw new Error(`Couldn't parse passthrough data: ${error}`) + } + } else if (flags.passthroughData) { + try { + passthrough = JSON.parse(flags.passthroughData) + } catch { + passthrough = flags.passthroughData } + } else { + throw new Error('One out of passthroughFile or passthroughData must be passed') + } - input.passthrough = passthrough + input.passthrough = passthrough - fs.writeFileSync(output, JSON.stringify(input, null, 2)) - } + fs.writeFileSync(output, JSON.stringify(input, null, 2)) + } } diff --git a/src/commands/supplement/target/read.ts b/src/commands/supplement/target/read.ts index ee67eee17..40949d9da 100644 --- a/src/commands/supplement/target/read.ts +++ b/src/commands/supplement/target/read.ts @@ -4,28 +4,28 @@ import fs from 'fs' import {BaseCommand} from '../../../utils/oclif/baseCommand' export default class ReadTarget extends BaseCommand { - static readonly usage = '<%= command.id %> -i [-o ]' + static readonly usage = '<%= command.id %> -i [-o ]' - static readonly description = 'Read the `target` attribute in a given Heimdall Data Format JSON file and send it to stdout or write it to a file' + static readonly description = 'Read the `target` attribute in a given Heimdall Data Format JSON file and send it to stdout or write it to a file' - static readonly examples = ['<%= config.bin %> <%= command.id %> -i hdf.json -o target.json'] + static readonly examples = ['<%= config.bin %> <%= command.id %> -i hdf.json -o target.json'] - static readonly flags = { - input: Flags.string({char: 'i', required: true, description: 'An input HDF file'}), - output: Flags.string({char: 'o', description: 'An output `target` JSON file (otherwise the data is sent to stdout)'}), - } + static readonly flags = { + input: Flags.string({char: 'i', required: true, description: 'An input HDF file'}), + output: Flags.string({char: 'o', description: 'An output `target` JSON file (otherwise the data is sent to stdout)'}), + } - async run() { - const {flags} = await this.parse(ReadTarget) + async run() { + const {flags} = await this.parse(ReadTarget) - const input: ExecJSON.Execution & {target?: unknown} = JSON.parse(fs.readFileSync(flags.input, 'utf8')) + const input: ExecJSON.Execution & {target?: unknown} = JSON.parse(fs.readFileSync(flags.input, 'utf8')) - const target = input.target || {} + const target = input.target || {} - if (flags.output) { - fs.writeFileSync(flags.output, JSON.stringify(target, null, 2)) - } else { - process.stdout.write(JSON.stringify(target, null, 2)) - } + if (flags.output) { + fs.writeFileSync(flags.output, JSON.stringify(target, null, 2)) + } else { + process.stdout.write(JSON.stringify(target, null, 2)) } + } } diff --git a/src/commands/supplement/target/write.ts b/src/commands/supplement/target/write.ts index 1f80a1b64..ece87310d 100644 --- a/src/commands/supplement/target/write.ts +++ b/src/commands/supplement/target/write.ts @@ -4,55 +4,55 @@ import fs from 'fs' import {BaseCommand} from '../../../utils/oclif/baseCommand' export default class WriteTarget extends BaseCommand { - static readonly usage = '<%= command.id %> -i (-f | -d ) [-o ]' - - static readonly summary = 'Overwrite the `target` attribute in a given HDF file with the provided `target` JSON data' - - static readonly description = 'Target data can be any context/structure. See sample ideas at https://github.com/mitre/saf/wiki/Supplement-HDF-files-with-additional-information-(ex.-%60passthrough%60,-%60target%60)' - - static examples = [ - { - description: '\x1B[93mProviding target-data\x1B[0m', - command: '<%= config.bin %> <%= command.id %> -i hdf.json -d \'{"a": 5}\'', - }, - { - description: '\x1B[93mUsing target-data file\x1B[0m', - command: '<%= config.bin %> <%= command.id %> -i hdf.json -f target.json -o new-hdf.json', - }, - ] - - static readonly flags = { - input: Flags.string({char: 'i', required: true, description: 'An input Heimdall Data Format file'}), - targetFile: Flags.string({char: 'f', exclusive: ['targetData'], description: 'An input target-data file (can contain any valid JSON); this flag or `targetData` must be provided'}), - targetData: Flags.string({char: 'd', exclusive: ['targetFile'], description: 'Input target-data (can be any valid JSON); this flag or `targetFile` must be provided'}), - output: Flags.string({char: 'o', description: 'An output Heimdall Data Format JSON file (otherwise the input file is overwritten)'}), - } - - async run() { - const {flags} = await this.parse(WriteTarget) - - const input: ExecJSON.Execution & {target?: unknown} = JSON.parse(fs.readFileSync(flags.input, 'utf8')) - const output: string = flags.output || flags.input - - let target: unknown - if (flags.targetFile) { - try { - target = JSON.parse(fs.readFileSync(flags.targetFile, 'utf8')) - } catch (error: unknown) { - throw new Error(`Couldn't parse target data: ${error}`) - } - } else if (flags.targetData) { - try { - target = JSON.parse(flags.targetData) - } catch { - target = flags.targetData - } - } else { - throw new Error('One out of targetFile or targetData must be passed') + static readonly usage = '<%= command.id %> -i (-f | -d ) [-o ]' + + static readonly summary = 'Overwrite the `target` attribute in a given HDF file with the provided `target` JSON data' + + static readonly description = 'Target data can be any context/structure. See sample ideas at https://github.com/mitre/saf/wiki/Supplement-HDF-files-with-additional-information-(ex.-%60passthrough%60,-%60target%60)' + + static examples = [ + { + description: '\x1B[93mProviding target-data\x1B[0m', + command: '<%= config.bin %> <%= command.id %> -i hdf.json -d \'{"a": 5}\'', + }, + { + description: '\x1B[93mUsing target-data file\x1B[0m', + command: '<%= config.bin %> <%= command.id %> -i hdf.json -f target.json -o new-hdf.json', + }, + ] + + static readonly flags = { + input: Flags.string({char: 'i', required: true, description: 'An input Heimdall Data Format file'}), + targetFile: Flags.string({char: 'f', exclusive: ['targetData'], description: 'An input target-data file (can contain any valid JSON); this flag or `targetData` must be provided'}), + targetData: Flags.string({char: 'd', exclusive: ['targetFile'], description: 'Input target-data (can be any valid JSON); this flag or `targetFile` must be provided'}), + output: Flags.string({char: 'o', description: 'An output Heimdall Data Format JSON file (otherwise the input file is overwritten)'}), + } + + async run() { + const {flags} = await this.parse(WriteTarget) + + const input: ExecJSON.Execution & {target?: unknown} = JSON.parse(fs.readFileSync(flags.input, 'utf8')) + const output: string = flags.output || flags.input + + let target: unknown + if (flags.targetFile) { + try { + target = JSON.parse(fs.readFileSync(flags.targetFile, 'utf8')) + } catch (error: unknown) { + throw new Error(`Couldn't parse target data: ${error}`) + } + } else if (flags.targetData) { + try { + target = JSON.parse(flags.targetData) + } catch { + target = flags.targetData } + } else { + throw new Error('One out of targetFile or targetData must be passed') + } - input.target = target + input.target = target - fs.writeFileSync(output, JSON.stringify(input, null, 2)) - } + fs.writeFileSync(output, JSON.stringify(input, null, 2)) + } }