From daaa26bb20f8d37fd1b2ae42502ad03c9a5c3f24 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Tue, 27 May 2025 16:17:43 +0200 Subject: [PATCH 01/24] feat(IaC): POC for IaC Provider --- iac/main.tf | 4 + poetry.lock | 1271 +++++++++++++++-- prowler/__main__.py | 84 +- prowler/compliance/iac/__init__.py | 0 prowler/config/config.py | 1 + prowler/lib/check/checks_loader.py | 4 + prowler/lib/check/compliance_models.py | 1 + prowler/lib/check/models.py | 36 + prowler/lib/check/utils.py | 8 + prowler/lib/cli/parser.py | 5 +- prowler/lib/outputs/finding.py | 8 + prowler/lib/outputs/summary_table.py | 3 + prowler/providers/common/models.py | 4 + prowler/providers/common/provider.py | 14 +- prowler/providers/iac/__init__.py | 0 prowler/providers/iac/iac.py | 3 + prowler/providers/iac/iac_provider.py | 166 +++ prowler/providers/iac/lib/__init__.py | 0 .../providers/iac/lib/arguments/__init__.py | 0 .../providers/iac/lib/arguments/arguments.py | 12 + prowler/providers/iac/models.py | 27 + pyproject.toml | 11 +- 22 files changed, 1493 insertions(+), 169 deletions(-) create mode 100644 iac/main.tf create mode 100644 prowler/compliance/iac/__init__.py create mode 100644 prowler/providers/iac/__init__.py create mode 100644 prowler/providers/iac/iac.py create mode 100644 prowler/providers/iac/iac_provider.py create mode 100644 prowler/providers/iac/lib/__init__.py create mode 100644 prowler/providers/iac/lib/arguments/__init__.py create mode 100644 prowler/providers/iac/lib/arguments/arguments.py create mode 100644 prowler/providers/iac/models.py diff --git a/iac/main.tf b/iac/main.tf new file mode 100644 index 0000000000..9b41a9fb3c --- /dev/null +++ b/iac/main.tf @@ -0,0 +1,4 @@ +resource "aws_s3_bucket" "bad_example" { + bucket = "my-unsafe-bucket" + acl = "public-read" +} diff --git a/poetry.lock b/poetry.lock index bea5bae5ff..55aa81a198 100644 --- a/poetry.lock +++ b/poetry.lock @@ -12,6 +12,21 @@ files = [ {file = "about_time-4.2.1-py3-none-any.whl", hash = "sha256:8bbf4c75fe13cbd3d72f49a03b02c5c7dca32169b6d49117c257e7eb3eaee341"}, ] +[[package]] +name = "aiodns" +version = "3.4.0" +description = "Simple DNS resolver for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiodns-3.4.0-py3-none-any.whl", hash = "sha256:4da2b25f7475343f3afbb363a2bfe46afa544f2b318acb9a945065e622f4ed24"}, + {file = "aiodns-3.4.0.tar.gz", hash = "sha256:24b0ae58410530367f21234d0c848e4de52c1f16fbddc111726a4ab536ec1b2f"}, +] + +[package.dependencies] +pycares = ">=4.0.0" + [[package]] name = "aiohappyeyeballs" version = "2.6.1" @@ -128,6 +143,22 @@ yarl = ">=1.17.0,<2.0" [package.extras] speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.2.0) ; sys_platform == \"linux\" or sys_platform == \"darwin\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +[[package]] +name = "aiomultiprocess" +version = "0.9.1" +description = "AsyncIO version of the standard multiprocessing module" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "aiomultiprocess-0.9.1-py3-none-any.whl", hash = "sha256:3a7b3bb3c38dbfb4d9d1194ece5934b6d32cf0280e8edbe64a7d215bba1322c6"}, + {file = "aiomultiprocess-0.9.1.tar.gz", hash = "sha256:f0231dbe0291e15325d7896ebeae0002d95a4f2675426ca05eb35f24c60e495b"}, +] + +[package.extras] +dev = ["attribution (==1.7.1)", "black (==24.4.0)", "coverage (==7.4.4)", "flake8 (==7.0.0)", "flake8-bugbear (==24.4.21)", "flit (==3.9.0)", "mypy (==1.9.0)", "usort (==1.0.8.post1)", "uvloop (==0.19.0) ; sys_platform != \"win32\""] +docs = ["sphinx (==7.3.7)", "sphinx-mdinclude (==0.6.0)"] + [[package]] name = "aiosignal" version = "1.3.2" @@ -159,6 +190,18 @@ files = [ about-time = "4.2.1" grapheme = "0.6.0" +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + [[package]] name = "antlr4-python3-runtime" version = "4.13.2" @@ -194,6 +237,39 @@ doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] trio = ["trio (>=0.26.1)"] +[[package]] +name = "argcomplete" +version = "3.6.2" +description = "Bash tab completion for argparse" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"}, + {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"}, +] + +[package.extras] +test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] + +[[package]] +name = "asteval" +version = "1.0.5" +description = "Safe, minimalistic evaluator of python expression using ast module" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "asteval-1.0.5-py3-none-any.whl", hash = "sha256:082b95312578affc8a6d982f7d92b7ac5de05634985c87e7eedd3188d31149fa"}, + {file = "asteval-1.0.5.tar.gz", hash = "sha256:bac3c8dd6d2b789e959cfec9bb296fb8338eec066feae618c462132701fbc665"}, +] + +[package.extras] +all = ["asteval[dev,doc,test]"] +dev = ["build", "twine"] +doc = ["Sphinx"] +test = ["coverage", "pytest", "pytest-cov"] + [[package]] name = "astroid" version = "3.3.9" @@ -751,6 +827,100 @@ test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", toml = ["tomli (>=1.1.0) ; python_version < \"3.11\""] yaml = ["PyYAML"] +[[package]] +name = "bc-detect-secrets" +version = "1.5.41" +description = "Tool for detecting secrets in the codebase" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "bc_detect_secrets-1.5.41-py3-none-any.whl", hash = "sha256:629df912f2a4f4d5039cc1fece906c34700586f7db1ae6a8d1c830c25df6db9b"}, + {file = "bc_detect_secrets-1.5.41.tar.gz", hash = "sha256:4bd08292a975bfc9b95771e118dd1131e1afbd479610eb29e4e0c15bd33677fc"}, +] + +[package.dependencies] +pyyaml = "*" +requests = "*" +unidiff = "*" + +[package.extras] +gibberish = ["gibberish-detector"] +word-list = ["pyahocorasick"] + +[[package]] +name = "bc-jsonpath-ng" +version = "1.6.1" +description = "A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "bc-jsonpath-ng-1.6.1.tar.gz", hash = "sha256:6ea4e379c4400a511d07605b8d981950292dd098a5619d143328af4e841a2320"}, + {file = "bc_jsonpath_ng-1.6.1-py3-none-any.whl", hash = "sha256:2c85bb1d194376808fe1fc49558dd484e39024b15c719995e22de811e6ba4dc8"}, +] + +[package.dependencies] +decorator = "*" +ply = "*" + +[[package]] +name = "bc-python-hcl2" +version = "0.4.2" +description = "A parser for HCL2" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "bc-python-hcl2-0.4.2.tar.gz", hash = "sha256:ac8ff59fb9bd437ea29b89a7d7c507fd0a1e957845bae9aeac69f2892b8d681e"}, + {file = "bc_python_hcl2-0.4.2-py3-none-any.whl", hash = "sha256:90d2afbaa2c7e77b7b30bf58180084e11d95287f7c3e19c5bfbdb54ab2fd80e9"}, +] + +[package.dependencies] +lark = ">=1.0.0" + +[[package]] +name = "beartype" +version = "0.21.0" +description = "Unbearably fast near-real-time hybrid runtime-static type-checking in pure Python." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "beartype-0.21.0-py3-none-any.whl", hash = "sha256:b6a1bd56c72f31b0a496a36cc55df6e2f475db166ad07fa4acc7e74f4c7f34c0"}, + {file = "beartype-0.21.0.tar.gz", hash = "sha256:f9a5078f5ce87261c2d22851d19b050b64f6a805439e8793aecf01ce660d3244"}, +] + +[package.extras] +dev = ["autoapi (>=0.9.0)", "click", "coverage (>=5.5)", "equinox ; sys_platform == \"linux\"", "jax[cpu] ; sys_platform == \"linux\"", "jaxtyping ; sys_platform == \"linux\"", "langchain", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\"", "numba ; python_version < \"3.13.0\"", "numpy ; sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera", "pydata-sphinx-theme (<=0.7.2)", "pygments", "pyright (>=1.1.370)", "pytest (>=4.0.0)", "rich-click", "sphinx", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)", "sqlalchemy", "tox (>=3.20.1)", "typing-extensions (>=3.10.0.0)", "xarray"] +doc-rtd = ["autoapi (>=0.9.0)", "pydata-sphinx-theme (<=0.7.2)", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)"] +test = ["click", "coverage (>=5.5)", "equinox ; sys_platform == \"linux\"", "jax[cpu] ; sys_platform == \"linux\"", "jaxtyping ; sys_platform == \"linux\"", "langchain", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\"", "numba ; python_version < \"3.13.0\"", "numpy ; sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera", "pygments", "pyright (>=1.1.370)", "pytest (>=4.0.0)", "rich-click", "sphinx", "sqlalchemy", "tox (>=3.20.1)", "typing-extensions (>=3.10.0.0)", "xarray"] +test-tox = ["click", "equinox ; sys_platform == \"linux\"", "jax[cpu] ; sys_platform == \"linux\"", "jaxtyping ; sys_platform == \"linux\"", "langchain", "mypy (>=0.800) ; platform_python_implementation != \"PyPy\"", "nuitka (>=1.2.6) ; sys_platform == \"linux\"", "numba ; python_version < \"3.13.0\"", "numpy ; sys_platform != \"darwin\" and platform_python_implementation != \"PyPy\"", "pandera", "pygments", "pyright (>=1.1.370)", "pytest (>=4.0.0)", "rich-click", "sphinx", "sqlalchemy", "typing-extensions (>=3.10.0.0)", "xarray"] +test-tox-coverage = ["coverage (>=5.5)"] + +[[package]] +name = "beautifulsoup4" +version = "4.13.4" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.7.0" +groups = ["main"] +files = [ + {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, + {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, +] + +[package.dependencies] +soupsieve = ">1.2" +typing-extensions = ">=4.0.0" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + [[package]] name = "black" version = "25.1.0" @@ -810,20 +980,38 @@ files = [ {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, ] +[[package]] +name = "boolean-py" +version = "5.0" +description = "Define boolean algebras, create and parse boolean expressions and create custom boolean DSL." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "boolean_py-5.0-py3-none-any.whl", hash = "sha256:ef28a70bd43115208441b53a045d1549e2f0ec6e3d08a9d142cbc41c1938e8d9"}, + {file = "boolean_py-5.0.tar.gz", hash = "sha256:60cbc4bad079753721d32649545505362c754e121570ada4658b852a3a318d95"}, +] + +[package.extras] +dev = ["build", "twine"] +docs = ["Sphinx (>=3.3.1)", "doc8 (>=0.8.1)", "sphinx-rtd-theme (>=0.5.0)", "sphinxcontrib-apidoc (>=0.3.0)"] +linting = ["black", "isort", "pycodestyle"] +testing = ["pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)"] + [[package]] name = "boto3" -version = "1.35.99" +version = "1.35.49" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "boto3-1.35.99-py3-none-any.whl", hash = "sha256:83e560faaec38a956dfb3d62e05e1703ee50432b45b788c09e25107c5058bd71"}, - {file = "boto3-1.35.99.tar.gz", hash = "sha256:e0abd794a7a591d90558e92e29a9f8837d25ece8e3c120e530526fe27eba5fca"}, + {file = "boto3-1.35.49-py3-none-any.whl", hash = "sha256:b660c649a27a6b47a34f6f858f5bd7c3b0a798a16dec8dda7cbebeee80fd1f60"}, + {file = "boto3-1.35.49.tar.gz", hash = "sha256:ddecb27f5699ca9f97711c52b6c0652c2e63bf6c2bfbc13b819b4f523b4d30ff"}, ] [package.dependencies] -botocore = ">=1.35.99,<1.36.0" +botocore = ">=1.35.49,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -853,6 +1041,18 @@ urllib3 = [ [package.extras] crt = ["awscrt (==0.22.0)"] +[[package]] +name = "cached-property" +version = "2.0.1" +description = "A decorator for caching properties in classes." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "cached_property-2.0.1-py3-none-any.whl", hash = "sha256:f617d70ab1100b7bcf6e42228f9ddcb78c676ffa167278d9f730d1c2fba69ccb"}, + {file = "cached_property-2.0.1.tar.gz", hash = "sha256:484d617105e3ee0e4f1f58725e72a8ef9e93deee462222dbd51cd91230897641"}, +] + [[package]] name = "cachetools" version = "5.5.2" @@ -1087,6 +1287,66 @@ files = [ {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] +[[package]] +name = "checkov" +version = "3.2.434" +description = "Infrastructure as code static analysis" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "checkov-3.2.434-py3-none-any.whl", hash = "sha256:87178226f16fe479ac7233119bb70721b8132d262045348269ccf76a9495d898"}, + {file = "checkov-3.2.434.tar.gz", hash = "sha256:32a295740717b4e629c733f17b25661bcc523d88d210111c5c1e63fc5e486852"}, +] + +[package.dependencies] +aiodns = ">=3.0.0,<4.0.0" +aiohttp = ">=3.8.0,<4.0.0" +aiomultiprocess = ">=0.9.0,<0.10.0" +argcomplete = ">=3.0.0,<4.0.0" +asteval = "1.0.5" +bc-detect-secrets = "1.5.41" +bc-jsonpath-ng = "1.6.1" +bc-python-hcl2 = "0.4.2" +boto3 = "1.35.49" +cachetools = ">=5.2.0,<6.0.0" +charset-normalizer = ">=3.1.0,<4.0.0" +click = ">=8.1.0,<9.0.0" +cloudsplaining = ">=0.7.0,<0.8.0" +colorama = ">=0.4.3,<0.5.0" +configargparse = ">=1.5.3,<2.0.0" +cyclonedx-python-lib = ">=6.0.0,<8.0.0" +docker = ">=6.0.1,<8.0.0" +dockerfile-parse = ">=2.0.0,<3.0.0" +dpath = "2.1.3" +gitpython = ">=3.1.30,<4.0.0" +importlib-metadata = ">=6.0.0,<8.0.0" +jmespath = ">=1.0.0,<2.0.0" +jsonschema = ">=4.17.0,<5.0.0" +junit-xml = ">=1.9,<2.0" +license-expression = ">=30.1.0,<31.0.0" +networkx = "<2.7" +packageurl-python = ">=0.11.1,<0.14.0" +packaging = ">=23.0,<24.0" +prettytable = ">=3.6.0,<4.0.0" +pycep-parser = "0.5.1" +pydantic = ">=2.0.0,<3.0.0" +pyston = {version = "2.3.5", markers = "python_version < \"3.11\" and (sys_platform == \"linux\" or sys_platform == \"darwin\") and platform_machine == \"x86_64\" and implementation_name == \"cpython\""} +pyston-autoload = {version = "2.3.5", markers = "python_version < \"3.11\" and (sys_platform == \"linux\" or sys_platform == \"darwin\") and platform_machine == \"x86_64\" and implementation_name == \"cpython\""} +pyyaml = ">=6.0.0,<7.0.0" +requests = ">=2.28.0,<3.0.0" +rustworkx = ">=0.13.0,<1.0.0" +schema = "<=0.7.5" +spdx-tools = ">=0.8.0,<0.9.0" +tabulate = ">=0.9.0,<0.10.0" +termcolor = ">=1.1.0,<2.4.0" +tqdm = ">=4.65.0,<5.0.0" +typing-extensions = ">=4.5.0,<5.0.0" +yarl = ">=1.9.1,<2.0.0" + +[package.extras] +dev = ["GitPython (==3.1.41)", "bandit", "coverage (==7.6.1)", "coverage-badge", "jsonschema", "pytest (<8.0.0)"] + [[package]] name = "click" version = "8.1.8" @@ -1102,6 +1362,27 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "click-option-group" +version = "0.5.7" +description = "Option groups missing in Click" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "click_option_group-0.5.7-py3-none-any.whl", hash = "sha256:96b9f52f397ef4d916f81929bd6c1f85e89046c7a401a64e72a61ae74ad35c24"}, + {file = "click_option_group-0.5.7.tar.gz", hash = "sha256:8dc780be038712fc12c9fecb3db4fe49e0d0723f9c171d7cda85c20369be693c"}, +] + +[package.dependencies] +click = ">=7.0" + +[package.extras] +dev = ["pre-commit", "pytest"] +docs = ["m2r2", "pallets-sphinx-themes", "sphinx"] +test = ["pytest"] +test-cov = ["pytest", "pytest-cov"] + [[package]] name = "click-plugins" version = "1.1.1" @@ -1120,6 +1401,30 @@ click = ">=4.0" [package.extras] dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] +[[package]] +name = "cloudsplaining" +version = "0.7.0" +description = "AWS IAM Security Assessment tool that identifies violations of least privilege and generates a risk-prioritized HTML report." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "cloudsplaining-0.7.0-py3-none-any.whl", hash = "sha256:8e93c7b1671c8353f520627cdf7917ec543581c9b9936b3d344817bb4747174e"}, + {file = "cloudsplaining-0.7.0.tar.gz", hash = "sha256:2d8a1d1a3261368a39359bb23aa7d6ac9add274728ff24877b710cdfa96d96af"}, +] + +[package.dependencies] +boto3 = "*" +botocore = "*" +cached-property = "*" +click = "*" +click-option-group = "*" +jinja2 = "*" +markdown = "*" +policy-sentry = ">=0.13.0,<0.14" +pyyaml = "*" +schema = "*" + [[package]] name = "colorama" version = "0.4.6" @@ -1132,6 +1437,34 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "configargparse" +version = "1.7.1" +description = "A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "configargparse-1.7.1-py3-none-any.whl", hash = "sha256:8b586a31f9d873abd1ca527ffbe58863c99f36d896e2829779803125e83be4b6"}, + {file = "configargparse-1.7.1.tar.gz", hash = "sha256:79c2ddae836a1e5914b71d58e4b9adbd9f7779d4e6351a637b7d2d9b6c46d3d9"}, +] + +[package.extras] +test = ["PyYAML", "mock", "pytest"] +yaml = ["PyYAML"] + +[[package]] +name = "contextlib2" +version = "21.6.0" +description = "Backports and enhancements for the contextlib module" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "contextlib2-21.6.0-py2.py3-none-any.whl", hash = "sha256:3fbdb64466afd23abaf6c977627b75b6139a5a3e8ce38405c5b413aed7a0471f"}, + {file = "contextlib2-21.6.0.tar.gz", hash = "sha256:ab1e2bfe1d01d968e1b7e8d9023bc51ef3509bba217bb730cee3827e1ee82869"}, +] + [[package]] name = "coverage" version = "7.6.12" @@ -1265,6 +1598,29 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.1)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "cyclonedx-python-lib" +version = "7.6.2" +description = "Python library for CycloneDX" +optional = false +python-versions = "<4.0,>=3.8" +groups = ["main"] +files = [ + {file = "cyclonedx_python_lib-7.6.2-py3-none-any.whl", hash = "sha256:c42fab352cc0f7418d1b30def6751d9067ebcf0e8e4be210fc14d6e742a9edcc"}, + {file = "cyclonedx_python_lib-7.6.2.tar.gz", hash = "sha256:31186c5725ac0cfcca433759a407b1424686cdc867b47cc86e6cf83691310903"}, +] + +[package.dependencies] +license-expression = ">=30,<31" +packageurl-python = ">=0.11,<2" +py-serializable = ">=1.1.0,<2.0.0" +sortedcontainers = ">=2.4.0,<3.0.0" + +[package.extras] +json-validation = ["jsonschema[format] (>=4.18,<5.0)"] +validation = ["jsonschema[format] (>=4.18,<5.0)", "lxml (>=4,<6)"] +xml-validation = ["lxml (>=4,<6)"] + [[package]] name = "dash" version = "2.18.2" @@ -1353,6 +1709,30 @@ files = [ {file = "dash_table-5.0.0.tar.gz", hash = "sha256:18624d693d4c8ef2ddec99a6f167593437a7ea0bf153aa20f318c170c5bc7308"}, ] +[[package]] +name = "decorator" +version = "5.2.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, + {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + [[package]] name = "deprecated" version = "1.2.18" @@ -1434,7 +1814,7 @@ version = "7.1.0" description = "A Python library for the Docker Engine API." optional = false python-versions = ">=3.8" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, @@ -1451,6 +1831,18 @@ docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"] ssh = ["paramiko (>=2.4.3)"] websockets = ["websocket-client (>=1.3.0)"] +[[package]] +name = "dockerfile-parse" +version = "2.0.1" +description = "Python library for Dockerfile manipulation" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "dockerfile-parse-2.0.1.tar.gz", hash = "sha256:3184ccdc513221983e503ac00e1aa504a2aa8f84e5de673c46b0b6eee99ec7bc"}, + {file = "dockerfile_parse-2.0.1-py2.py3-none-any.whl", hash = "sha256:bdffd126d2eb26acf1066acb54cb2e336682e1d72b974a40894fac76a4df17f6"}, +] + [[package]] name = "dparse" version = "0.6.4" @@ -1473,6 +1865,18 @@ conda = ["pyyaml"] pipenv = ["pipenv"] poetry = ["poetry"] +[[package]] +name = "dpath" +version = "2.1.3" +description = "Filesystem-like pathing and searching for dictionaries" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "dpath-2.1.3-py3-none-any.whl", hash = "sha256:d9560e03ccd83b3c6f29988b0162ce9b34fd28b9d8dbda46663b20c68d9cdae3"}, + {file = "dpath-2.1.3.tar.gz", hash = "sha256:d1a7a0e6427d0a4156c792c82caf1f0109603f68ace792e36ca4596fd2cb8d9d"}, +] + [[package]] name = "durationpy" version = "0.9" @@ -1743,7 +2147,7 @@ version = "4.0.12" description = "Git Object Database" optional = false python-versions = ">=3.7" -groups = ["docs"] +groups = ["main", "docs"] files = [ {file = "gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf"}, {file = "gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571"}, @@ -1758,7 +2162,7 @@ version = "3.1.44" description = "GitPython is a Python library used to interact with Git repositories" optional = false python-versions = ">=3.7" -groups = ["docs"] +groups = ["main", "docs"] files = [ {file = "GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110"}, {file = "gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269"}, @@ -2037,28 +2441,24 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "importlib-metadata" -version = "8.6.1" +version = "7.2.1" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.9" +python-versions = ">=3.8" groups = ["main", "dev", "docs"] files = [ - {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"}, - {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"}, + {file = "importlib_metadata-7.2.1-py3-none-any.whl", hash = "sha256:ffef94b0b66046dd8ea2d619b701fe978d9264d38f3998bc4c27ec3b146a87c8"}, + {file = "importlib_metadata-7.2.1.tar.gz", hash = "sha256:509ecb2ab77071db5137c655e24ceb3eee66e7bbc6574165d0d114d9fc4bbe68"}, ] markers = {dev = "python_version < \"3.10\"", docs = "python_version < \"3.10\""} [package.dependencies] -zipp = ">=3.20" +zipp = ">=0.5" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""] -cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] +test = ["flufl.flake8", "importlib-resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" @@ -2184,8 +2584,6 @@ python-versions = "*" groups = ["dev"] files = [ {file = "jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c"}, - {file = "jsonpath_ng-1.7.0-py2-none-any.whl", hash = "sha256:898c93fc173f0c336784a3fa63d7434297544b7198124a68f9a3ef9597b0ae6e"}, - {file = "jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6"}, ] [package.dependencies] @@ -2258,6 +2656,21 @@ files = [ [package.dependencies] referencing = ">=0.31.0" +[[package]] +name = "junit-xml" +version = "1.9" +description = "Creates JUnit XML test result documents that can be read by tools such as Jenkins" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "junit-xml-1.9.tar.gz", hash = "sha256:de16a051990d4e25a3982b2dd9e89d671067548718866416faec14d9de56db9f"}, + {file = "junit_xml-1.9-py2.py3-none-any.whl", hash = "sha256:ec5ca1a55aefdd76d28fcc0b135251d156c7106fa979686a4b48d62b761b4732"}, +] + +[package.dependencies] +six = "*" + [[package]] name = "kubernetes" version = "32.0.1" @@ -2286,6 +2699,24 @@ websocket-client = ">=0.32.0,<0.40.0 || >0.40.0,<0.41.dev0 || >=0.43.dev0" [package.extras] adal = ["adal (>=1.0.2)"] +[[package]] +name = "lark" +version = "1.2.2" +description = "a modern parsing library" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c"}, + {file = "lark-1.2.2.tar.gz", hash = "sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80"}, +] + +[package.extras] +atomic-cache = ["atomicwrites"] +interegular = ["interegular (>=0.3.1,<0.4.0)"] +nearley = ["js2py"] +regex = ["regex"] + [[package]] name = "lazy-object-proxy" version = "1.11.0" @@ -2310,13 +2741,32 @@ files = [ {file = "lazy_object_proxy-1.11.0.tar.gz", hash = "sha256:18874411864c9fbbbaa47f9fc1dd7aea754c86cfde21278ef427639d1dd78e9c"}, ] +[[package]] +name = "license-expression" +version = "30.4.1" +description = "license-expression is a comprehensive utility library to parse, compare, simplify and normalize license expressions (such as SPDX license expressions) using boolean logic." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "license_expression-30.4.1-py3-none-any.whl", hash = "sha256:679646bc3261a17690494a3e1cada446e5ee342dbd87dcfa4a0c24cc5dce13ee"}, + {file = "license_expression-30.4.1.tar.gz", hash = "sha256:9f02105f9e0fcecba6a85dfbbed7d94ea1c3a70cf23ddbfb5adf3438a6f6fce0"}, +] + +[package.dependencies] +"boolean.py" = ">=4.0" + +[package.extras] +docs = ["Sphinx (>=5.0.2)", "doc8 (>=0.11.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-reredirects (>=0.1.2)", "sphinx-rtd-dark-mode (>=1.3.0)", "sphinx-rtd-theme (>=1.0.0)", "sphinxcontrib-apidoc (>=0.4.0)"] +testing = ["black", "isort", "pytest (>=6,!=7.0.0)", "pytest-xdist (>=2)", "twine"] + [[package]] name = "markdown" version = "3.8" description = "Python implementation of John Gruber's Markdown." optional = false python-versions = ">=3.9" -groups = ["docs"] +groups = ["main", "docs"] files = [ {file = "markdown-3.8-py3-none-any.whl", hash = "sha256:794a929b79c5af141ef5ab0f2f642d0f7b1872981250230e72682346f7cc90dc"}, {file = "markdown-3.8.tar.gz", hash = "sha256:7df81e63f0df5c4b24b7d156eb81e4690595239b7d70937d0409f1b0de319c6f"}, @@ -3087,44 +3537,22 @@ files = [ [[package]] name = "networkx" -version = "3.2.1" +version = "2.6.3" description = "Python package for creating and manipulating graphs and networks" optional = false -python-versions = ">=3.9" -groups = ["dev"] -markers = "python_version < \"3.10\"" +python-versions = ">=3.7" +groups = ["main", "dev"] files = [ - {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, - {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, + {file = "networkx-2.6.3-py3-none-any.whl", hash = "sha256:80b6b89c77d1dfb64a4c7854981b60aeea6360ac02c6d4e4913319e0a313abef"}, + {file = "networkx-2.6.3.tar.gz", hash = "sha256:c0946ed31d71f1b732b5aaa6da5a0388a345019af232ce2f49c766e2d6795c51"}, ] [package.extras] -default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] -developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] -doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] -extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"] -test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] - -[[package]] -name = "networkx" -version = "3.4.2" -description = "Python package for creating and manipulating graphs and networks" -optional = false -python-versions = ">=3.10" -groups = ["dev"] -markers = "python_version >= \"3.10\"" -files = [ - {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, - {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, -] - -[package.extras] -default = ["matplotlib (>=3.7)", "numpy (>=1.24)", "pandas (>=2.0)", "scipy (>=1.10,!=1.11.0,!=1.11.1)"] -developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] -doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.15)", "sphinx (>=7.3)", "sphinx-gallery (>=0.16)", "texext (>=0.6.7)"] -example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=1.9)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] -extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] -test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] +default = ["matplotlib (>=3.3)", "numpy (>=1.19)", "pandas (>=1.1)", "scipy (>=1.5,!=1.6.1)"] +developer = ["black (==21.5b1)", "pre-commit (>=2.12)"] +doc = ["nb2plots (>=0.6)", "numpydoc (>=1.1)", "pillow (>=8.2)", "pydata-sphinx-theme (>=0.6,<1.0)", "sphinx (>=4.0,<5.0)", "sphinx-gallery (>=0.9,<1.0)", "texext (>=0.6.6)"] +extra = ["lxml (>=4.5)", "pydot (>=1.4.1)", "pygraphviz (>=1.7)"] +test = ["codecov (>=2.1)", "pytest (>=6.2)", "pytest-cov (>=2.12)"] [[package]] name = "numpy" @@ -3282,16 +3710,116 @@ files = [ deprecated = ">=1.2.6" opentelemetry-api = "1.32.1" +[[package]] +name = "orjson" +version = "3.10.18" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "orjson-3.10.18-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a45e5d68066b408e4bc383b6e4ef05e717c65219a9e1390abc6155a520cac402"}, + {file = "orjson-3.10.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be3b9b143e8b9db05368b13b04c84d37544ec85bb97237b3a923f076265ec89c"}, + {file = "orjson-3.10.18-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9b0aa09745e2c9b3bf779b096fa71d1cc2d801a604ef6dd79c8b1bfef52b2f92"}, + {file = "orjson-3.10.18-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53a245c104d2792e65c8d225158f2b8262749ffe64bc7755b00024757d957a13"}, + {file = "orjson-3.10.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9495ab2611b7f8a0a8a505bcb0f0cbdb5469caafe17b0e404c3c746f9900469"}, + {file = "orjson-3.10.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73be1cbcebadeabdbc468f82b087df435843c809cd079a565fb16f0f3b23238f"}, + {file = "orjson-3.10.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8936ee2679e38903df158037a2f1c108129dee218975122e37847fb1d4ac68"}, + {file = "orjson-3.10.18-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7115fcbc8525c74e4c2b608129bef740198e9a120ae46184dac7683191042056"}, + {file = "orjson-3.10.18-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:771474ad34c66bc4d1c01f645f150048030694ea5b2709b87d3bda273ffe505d"}, + {file = "orjson-3.10.18-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c14047dbbea52886dd87169f21939af5d55143dad22d10db6a7514f058156a8"}, + {file = "orjson-3.10.18-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:641481b73baec8db14fdf58f8967e52dc8bda1f2aba3aa5f5c1b07ed6df50b7f"}, + {file = "orjson-3.10.18-cp310-cp310-win32.whl", hash = "sha256:607eb3ae0909d47280c1fc657c4284c34b785bae371d007595633f4b1a2bbe06"}, + {file = "orjson-3.10.18-cp310-cp310-win_amd64.whl", hash = "sha256:8770432524ce0eca50b7efc2a9a5f486ee0113a5fbb4231526d414e6254eba92"}, + {file = "orjson-3.10.18-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e0a183ac3b8e40471e8d843105da6fbe7c070faab023be3b08188ee3f85719b8"}, + {file = "orjson-3.10.18-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5ef7c164d9174362f85238d0cd4afdeeb89d9e523e4651add6a5d458d6f7d42d"}, + {file = "orjson-3.10.18-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd14c5d99cdc7bf93f22b12ec3b294931518aa019e2a147e8aa2f31fd3240f7"}, + {file = "orjson-3.10.18-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b672502323b6cd133c4af6b79e3bea36bad2d16bca6c1f645903fce83909a7a"}, + {file = "orjson-3.10.18-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51f8c63be6e070ec894c629186b1c0fe798662b8687f3d9fdfa5e401c6bd7679"}, + {file = "orjson-3.10.18-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9478ade5313d724e0495d167083c6f3be0dd2f1c9c8a38db9a9e912cdaf947"}, + {file = "orjson-3.10.18-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:187aefa562300a9d382b4b4eb9694806e5848b0cedf52037bb5c228c61bb66d4"}, + {file = "orjson-3.10.18-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da552683bc9da222379c7a01779bddd0ad39dd699dd6300abaf43eadee38334"}, + {file = "orjson-3.10.18-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e450885f7b47a0231979d9c49b567ed1c4e9f69240804621be87c40bc9d3cf17"}, + {file = "orjson-3.10.18-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5e3c9cc2ba324187cd06287ca24f65528f16dfc80add48dc99fa6c836bb3137e"}, + {file = "orjson-3.10.18-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:50ce016233ac4bfd843ac5471e232b865271d7d9d44cf9d33773bcd883ce442b"}, + {file = "orjson-3.10.18-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b3ceff74a8f7ffde0b2785ca749fc4e80e4315c0fd887561144059fb1c138aa7"}, + {file = "orjson-3.10.18-cp311-cp311-win32.whl", hash = "sha256:fdba703c722bd868c04702cac4cb8c6b8ff137af2623bc0ddb3b3e6a2c8996c1"}, + {file = "orjson-3.10.18-cp311-cp311-win_amd64.whl", hash = "sha256:c28082933c71ff4bc6ccc82a454a2bffcef6e1d7379756ca567c772e4fb3278a"}, + {file = "orjson-3.10.18-cp311-cp311-win_arm64.whl", hash = "sha256:a6c7c391beaedd3fa63206e5c2b7b554196f14debf1ec9deb54b5d279b1b46f5"}, + {file = "orjson-3.10.18-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:50c15557afb7f6d63bc6d6348e0337a880a04eaa9cd7c9d569bcb4e760a24753"}, + {file = "orjson-3.10.18-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:356b076f1662c9813d5fa56db7d63ccceef4c271b1fb3dd522aca291375fcf17"}, + {file = "orjson-3.10.18-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:559eb40a70a7494cd5beab2d73657262a74a2c59aff2068fdba8f0424ec5b39d"}, + {file = "orjson-3.10.18-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f3c29eb9a81e2fbc6fd7ddcfba3e101ba92eaff455b8d602bf7511088bbc0eae"}, + {file = "orjson-3.10.18-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6612787e5b0756a171c7d81ba245ef63a3533a637c335aa7fcb8e665f4a0966f"}, + {file = "orjson-3.10.18-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ac6bd7be0dcab5b702c9d43d25e70eb456dfd2e119d512447468f6405b4a69c"}, + {file = "orjson-3.10.18-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f72f100cee8dde70100406d5c1abba515a7df926d4ed81e20a9730c062fe9ad"}, + {file = "orjson-3.10.18-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca85398d6d093dd41dc0983cbf54ab8e6afd1c547b6b8a311643917fbf4e0c"}, + {file = "orjson-3.10.18-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22748de2a07fcc8781a70edb887abf801bb6142e6236123ff93d12d92db3d406"}, + {file = "orjson-3.10.18-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3a83c9954a4107b9acd10291b7f12a6b29e35e8d43a414799906ea10e75438e6"}, + {file = "orjson-3.10.18-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:303565c67a6c7b1f194c94632a4a39918e067bd6176a48bec697393865ce4f06"}, + {file = "orjson-3.10.18-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:86314fdb5053a2f5a5d881f03fca0219bfdf832912aa88d18676a5175c6916b5"}, + {file = "orjson-3.10.18-cp312-cp312-win32.whl", hash = "sha256:187ec33bbec58c76dbd4066340067d9ece6e10067bb0cc074a21ae3300caa84e"}, + {file = "orjson-3.10.18-cp312-cp312-win_amd64.whl", hash = "sha256:f9f94cf6d3f9cd720d641f8399e390e7411487e493962213390d1ae45c7814fc"}, + {file = "orjson-3.10.18-cp312-cp312-win_arm64.whl", hash = "sha256:3d600be83fe4514944500fa8c2a0a77099025ec6482e8087d7659e891f23058a"}, + {file = "orjson-3.10.18-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:69c34b9441b863175cc6a01f2935de994025e773f814412030f269da4f7be147"}, + {file = "orjson-3.10.18-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:1ebeda919725f9dbdb269f59bc94f861afbe2a27dce5608cdba2d92772364d1c"}, + {file = "orjson-3.10.18-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5adf5f4eed520a4959d29ea80192fa626ab9a20b2ea13f8f6dc58644f6927103"}, + {file = "orjson-3.10.18-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7592bb48a214e18cd670974f289520f12b7aed1fa0b2e2616b8ed9e069e08595"}, + {file = "orjson-3.10.18-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f872bef9f042734110642b7a11937440797ace8c87527de25e0c53558b579ccc"}, + {file = "orjson-3.10.18-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0315317601149c244cb3ecef246ef5861a64824ccbcb8018d32c66a60a84ffbc"}, + {file = "orjson-3.10.18-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0da26957e77e9e55a6c2ce2e7182a36a6f6b180ab7189315cb0995ec362e049"}, + {file = "orjson-3.10.18-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb70d489bc79b7519e5803e2cc4c72343c9dc1154258adf2f8925d0b60da7c58"}, + {file = "orjson-3.10.18-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9e86a6af31b92299b00736c89caf63816f70a4001e750bda179e15564d7a034"}, + {file = "orjson-3.10.18-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c382a5c0b5931a5fc5405053d36c1ce3fd561694738626c77ae0b1dfc0242ca1"}, + {file = "orjson-3.10.18-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8e4b2ae732431127171b875cb2668f883e1234711d3c147ffd69fe5be51a8012"}, + {file = "orjson-3.10.18-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d808e34ddb24fc29a4d4041dcfafbae13e129c93509b847b14432717d94b44f"}, + {file = "orjson-3.10.18-cp313-cp313-win32.whl", hash = "sha256:ad8eacbb5d904d5591f27dee4031e2c1db43d559edb8f91778efd642d70e6bea"}, + {file = "orjson-3.10.18-cp313-cp313-win_amd64.whl", hash = "sha256:aed411bcb68bf62e85588f2a7e03a6082cc42e5a2796e06e72a962d7c6310b52"}, + {file = "orjson-3.10.18-cp313-cp313-win_arm64.whl", hash = "sha256:f54c1385a0e6aba2f15a40d703b858bedad36ded0491e55d35d905b2c34a4cc3"}, + {file = "orjson-3.10.18-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c95fae14225edfd699454e84f61c3dd938df6629a00c6ce15e704f57b58433bb"}, + {file = "orjson-3.10.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5232d85f177f98e0cefabb48b5e7f60cff6f3f0365f9c60631fecd73849b2a82"}, + {file = "orjson-3.10.18-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2783e121cafedf0d85c148c248a20470018b4ffd34494a68e125e7d5857655d1"}, + {file = "orjson-3.10.18-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e54ee3722caf3db09c91f442441e78f916046aa58d16b93af8a91500b7bbf273"}, + {file = "orjson-3.10.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2daf7e5379b61380808c24f6fc182b7719301739e4271c3ec88f2984a2d61f89"}, + {file = "orjson-3.10.18-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f39b371af3add20b25338f4b29a8d6e79a8c7ed0e9dd49e008228a065d07781"}, + {file = "orjson-3.10.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b819ed34c01d88c6bec290e6842966f8e9ff84b7694632e88341363440d4cc0"}, + {file = "orjson-3.10.18-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2f6c57debaef0b1aa13092822cbd3698a1fb0209a9ea013a969f4efa36bdea57"}, + {file = "orjson-3.10.18-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:755b6d61ffdb1ffa1e768330190132e21343757c9aa2308c67257cc81a1a6f5a"}, + {file = "orjson-3.10.18-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ce8d0a875a85b4c8579eab5ac535fb4b2a50937267482be402627ca7e7570ee3"}, + {file = "orjson-3.10.18-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57b5d0673cbd26781bebc2bf86f99dd19bd5a9cb55f71cc4f66419f6b50f3d77"}, + {file = "orjson-3.10.18-cp39-cp39-win32.whl", hash = "sha256:951775d8b49d1d16ca8818b1f20c4965cae9157e7b562a2ae34d3967b8f21c8e"}, + {file = "orjson-3.10.18-cp39-cp39-win_amd64.whl", hash = "sha256:fdd9d68f83f0bc4406610b1ac68bdcded8c5ee58605cc69e643a06f4d075f429"}, + {file = "orjson-3.10.18.tar.gz", hash = "sha256:e8da3947d92123eda795b68228cafe2724815621fe35e8e320a9e9593a4bcd53"}, +] + +[[package]] +name = "packageurl-python" +version = "0.13.4" +description = "A purl aka. Package URL parser and builder" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "packageurl-python-0.13.4.tar.gz", hash = "sha256:6eb5e995009cc73387095e0b507ab65df51357d25ddc5fce3d3545ad6dcbbee8"}, + {file = "packageurl_python-0.13.4-py3-none-any.whl", hash = "sha256:62aa13d60a0082ff115784fefdfe73a12f310e455365cca7c6d362161067f35f"}, +] + +[package.extras] +build = ["setuptools", "wheel"] +lint = ["black", "isort", "mypy"] +sqlalchemy = ["sqlalchemy (>=2.0.0)"] +test = ["pytest"] + [[package]] name = "packaging" -version = "25.0" +version = "23.2" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" groups = ["main", "dev", "docs"] files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] @@ -3494,12 +4022,50 @@ version = "3.11" description = "Python Lex & Yacc" optional = false python-versions = "*" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, ] +[[package]] +name = "policy-sentry" +version = "0.13.2" +description = "Generate locked-down AWS IAM Policies" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "policy_sentry-0.13.2-py3-none-any.whl", hash = "sha256:e82c3bc1783606449399c4221f67d05f6b08d8a184ba2fee87d04541d7282b86"}, + {file = "policy_sentry-0.13.2.tar.gz", hash = "sha256:db2b39f92989077f83fc4dd1d064e3ff20b69cfed82168ebdc060e7dce292e77"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +click = "*" +orjson = "*" +PyYAML = "*" +requests = "*" +schema = "*" + +[[package]] +name = "prettytable" +version = "3.16.0" +description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "prettytable-3.16.0-py3-none-any.whl", hash = "sha256:b5eccfabb82222f5aa46b798ff02a8452cf530a352c31bddfa29be41242863aa"}, + {file = "prettytable-3.16.0.tar.gz", hash = "sha256:3c64b31719d961bf69c9a7e03d0c1e477320906a98da63952bc6698d6164ff57"}, +] + +[package.dependencies] +wcwidth = "*" + +[package.extras] +tests = ["pytest", "pytest-cov", "pytest-lazy-fixtures"] + [[package]] name = "propcache" version = "0.3.1" @@ -3677,20 +4243,20 @@ test = ["enum34 ; python_version <= \"3.4\"", "ipaddress ; python_version < \"3. [[package]] name = "py-ocsf-models" -version = "0.3.1" +version = "0.5.0" description = "This is a Python implementation of the OCSF models. The models are used to represent the data of the OCSF Schema defined in https://schema.ocsf.io/." optional = false -python-versions = "<3.13,>3.9.1" +python-versions = "<3.14,>3.9.1" groups = ["main"] files = [ - {file = "py_ocsf_models-0.3.1-py3-none-any.whl", hash = "sha256:e722d567a7f3e5190fdd053c2e75a69cf33fab6f5c0a4b7de678768ba340ae3a"}, - {file = "py_ocsf_models-0.3.1.tar.gz", hash = "sha256:60defd2cc86e8882f42dc9c6dacca6dc16d6bc05f9477c2a3486a0d4b5882b94"}, + {file = "py_ocsf_models-0.5.0-py3-none-any.whl", hash = "sha256:7933253f56782c04c412d976796db429577810b951fe4195351794500b5962d8"}, + {file = "py_ocsf_models-0.5.0.tar.gz", hash = "sha256:bf05e955809d1ec3ab1007e4a4b2a8a0afa74b6e744ea8ffbf386e46b3af0a76"}, ] [package.dependencies] cryptography = "44.0.1" email-validator = "2.2.0" -pydantic = "1.10.21" +pydantic = ">=2.9.2,<3.0.0" [[package]] name = "py-partiql-parser" @@ -3707,6 +4273,21 @@ files = [ [package.extras] dev = ["black (==22.6.0)", "flake8", "mypy", "pytest"] +[[package]] +name = "py-serializable" +version = "1.1.2" +description = "Library for serializing and deserializing Python Objects to and from JSON and XML." +optional = false +python-versions = "<4.0,>=3.8" +groups = ["main"] +files = [ + {file = "py_serializable-1.1.2-py3-none-any.whl", hash = "sha256:801be61b0a1ba64c3861f7c624f1de5cfbbabf8b458acc9cdda91e8f7e5effa1"}, + {file = "py_serializable-1.1.2.tar.gz", hash = "sha256:89af30bc319047d4aa0d8708af412f6ce73835e18bacf1a080028bb9e2f42bdb"}, +] + +[package.dependencies] +defusedxml = ">=0.7.1,<0.8.0" + [[package]] name = "pyasn1" version = "0.6.1" @@ -3734,6 +4315,110 @@ files = [ [package.dependencies] pyasn1 = ">=0.6.1,<0.7.0" +[[package]] +name = "pycares" +version = "4.8.0" +description = "Python interface for c-ares" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pycares-4.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f40d9f4a8de398b110fdf226cdfadd86e8c7eb71d5298120ec41cf8d94b0012f"}, + {file = "pycares-4.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:339de06fc849a51015968038d2bbed68fc24047522404af9533f32395ca80d25"}, + {file = "pycares-4.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372a236c1502b9056b0bea195c64c329603b4efa70b593a33b7ae37fbb7fad00"}, + {file = "pycares-4.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03f66a5e143d102ccc204bd4e29edd70bed28420f707efd2116748241e30cb73"}, + {file = "pycares-4.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef50504296cd5fc58cfd6318f82e20af24fbe2c83004f6ff16259adb13afdf14"}, + {file = "pycares-4.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1bc541b627c7951dd36136b18bd185c5244a0fb2af5b1492ffb8acaceec1c5b"}, + {file = "pycares-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:938d188ed6bed696099be67ebdcdf121827b9432b17a9ea9e40dc35fd9d85363"}, + {file = "pycares-4.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:327837ffdc0c7adda09c98e1263c64b2aff814eea51a423f66733c75ccd9a642"}, + {file = "pycares-4.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a6b9b8d08c4508c45bd39e0c74e9e7052736f18ca1d25a289365bb9ac36e5849"}, + {file = "pycares-4.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:feac07d5e6d2d8f031c71237c21c21b8c995b41a1eba64560e8cf1e42ac11bc6"}, + {file = "pycares-4.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5bcdbf37012fd2323ca9f2a1074421a9ccf277d772632f8f0ce8c46ec7564250"}, + {file = "pycares-4.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e3ebb692cb43fcf34fe0d26f2cf9a0ea53fdfb136463845b81fad651277922db"}, + {file = "pycares-4.8.0-cp310-cp310-win32.whl", hash = "sha256:d98447ec0efff3fa868ccc54dcc56e71faff498f8848ecec2004c3108efb4da2"}, + {file = "pycares-4.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:1abb8f40917960ead3c2771277f0bdee1967393b0fdf68743c225b606787da68"}, + {file = "pycares-4.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e25db89005ddd8d9c5720293afe6d6dd92e682fc6bc7a632535b84511e2060d"}, + {file = "pycares-4.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f9665ef116e6ee216c396f5f927756c2164f9f3316aec7ff1a9a1e1e7ec9b2a"}, + {file = "pycares-4.8.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54a96893133471f6889b577147adcc21a480dbe316f56730871028379c8313f3"}, + {file = "pycares-4.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51024b3a69762bd3100d94986a29922be15e13f56f991aaefb41f5bcd3d7f0bb"}, + {file = "pycares-4.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47ff9db50c599e4d965ae3bec99cc30941c1d2b0f078ec816680b70d052dd54a"}, + {file = "pycares-4.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27ef8ff4e0f60ea6769a60d1c3d1d2aefed1d832e7bb83fc3934884e2dba5cdd"}, + {file = "pycares-4.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63511af7a3f9663f562fbb6bfa3591a259505d976e2aba1fa2da13dde43c6ca7"}, + {file = "pycares-4.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:73c3219b47616e6a5ad1810de96ed59721c7751f19b70ae7bf24997a8365408f"}, + {file = "pycares-4.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:da42a45207c18f37be5e491c14b6d1063cfe1e46620eb661735d0cedc2b59099"}, + {file = "pycares-4.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8a068e898bb5dd09cd654e19cd2abf20f93d0cc59d5d955135ed48ea0f806aa1"}, + {file = "pycares-4.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:962aed95675bb66c0b785a2fbbd1bb58ce7f009e283e4ef5aaa4a1f2dc00d217"}, + {file = "pycares-4.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ce8b1a16c1e4517a82a0ebd7664783a327166a3764d844cf96b1fb7b9dd1e493"}, + {file = "pycares-4.8.0-cp311-cp311-win32.whl", hash = "sha256:b3749ddbcbd216376c3b53d42d8b640b457133f1a12b0e003f3838f953037ae7"}, + {file = "pycares-4.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:5ce8a4e1b485b2360ab666c4ea1db97f57ede345a3b566d80bfa52b17e616610"}, + {file = "pycares-4.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3273e01a75308ed06d2492d83c7ba476e579a60a24d9f20fe178ce5e9d8d028b"}, + {file = "pycares-4.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fcedaadea1f452911fd29935749f98d144dae758d6003b7e9b6c5d5bd47d1dff"}, + {file = "pycares-4.8.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aae6cb33e287e06a4aabcbc57626df682c9a4fa8026207f5b498697f1c2fb562"}, + {file = "pycares-4.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25038b930e5be82839503fb171385b2aefd6d541bc5b7da0938bdb67780467d2"}, + {file = "pycares-4.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cc8499b6e7dfbe4af65f6938db710ce9acd1debf34af2cbb93b898b1e5da6a5a"}, + {file = "pycares-4.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4e1c6a68ef56a7622f6176d9946d4e51f3c853327a0123ef35a5380230c84cd"}, + {file = "pycares-4.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7cc8c3c9114b9c84e4062d25ca9b4bddc80a65d0b074c7cb059275273382f89"}, + {file = "pycares-4.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4404014069d3e362abf404c9932d4335bb9c07ba834cfe7d683c725b92e0f9da"}, + {file = "pycares-4.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ee0a58c32ec2a352cef0e1d20335a7caf9871cd79b73be2ca2896fe70f09c9d7"}, + {file = "pycares-4.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:35f32f52b486b8fede3cbebf088f30b01242d0321b5216887c28e80490595302"}, + {file = "pycares-4.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ecbb506e27a3b3a2abc001c77beeccf265475c84b98629a6b3e61bd9f2987eaa"}, + {file = "pycares-4.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9392b2a34adbf60cb9e38f4a0d363413ecea8d835b5a475122f50f76676d59dd"}, + {file = "pycares-4.8.0-cp312-cp312-win32.whl", hash = "sha256:f0fbefe68403ffcff19c869b8d621c88a6d2cef18d53cf0dab0fa9458a6ca712"}, + {file = "pycares-4.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa8aab6085a2ddfb1b43a06ddf1b498347117bb47cd620d9b12c43383c9c2737"}, + {file = "pycares-4.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:358a9a2c6fed59f62788e63d88669224955443048a1602016d4358e92aedb365"}, + {file = "pycares-4.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e3e1278967fa8d4a0056be3fcc8fc551b8bad1fc7d0e5172196dccb8ddb036a"}, + {file = "pycares-4.8.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79befb773e370a8f97de9f16f5ea2c7e7fa0e3c6c74fbea6d332bf58164d7d06"}, + {file = "pycares-4.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b00d3695db64ce98a34e632e1d53f5a1cdb25451489f227bec2a6c03ff87ee8"}, + {file = "pycares-4.8.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37bdc4f2ff0612d60fc4f7547e12ff02cdcaa9a9e42e827bb64d4748994719f1"}, + {file = "pycares-4.8.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd92c44498ec7a6139888b464b28c49f7ba975933689bd67ea8d572b94188404"}, + {file = "pycares-4.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2665a0d810e2bbc41e97f3c3e5ea7950f666b3aa19c5f6c99d6b018ccd2e0052"}, + {file = "pycares-4.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45a629a6470a33478514c566bce50c63f1b17d1c5f2f964c9a6790330dc105fb"}, + {file = "pycares-4.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:47bb378f1773f41cca8e31dcdf009ce4a9b8aff8a30c7267aaff9a099c407ba5"}, + {file = "pycares-4.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fb3feae38458005cc101956e38f16eb3145fff8cd793e35cd4bdef6bf1aa2623"}, + {file = "pycares-4.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:14bc28aeaa66b0f4331ac94455e8043c8a06b3faafd78cc49d4b677bae0d0b08"}, + {file = "pycares-4.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62c82b871470f2864a1febf7b96bb1d108ce9063e6d3d43727e8a46f0028a456"}, + {file = "pycares-4.8.0-cp313-cp313-win32.whl", hash = "sha256:01afa8964c698c8f548b46d726f766aa7817b2d4386735af1f7996903d724920"}, + {file = "pycares-4.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:22f86f81b12ab17b0a7bd0da1e27938caaed11715225c1168763af97f8bb51a7"}, + {file = "pycares-4.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:61325d13a95255e858f42a7a1a9e482ff47ef2233f95ad9a4f308a3bd8ecf903"}, + {file = "pycares-4.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dfec3a7d42336fa46a1e7e07f67000fd4b97860598c59a894c08f81378629e4e"}, + {file = "pycares-4.8.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b65067e4b4f5345688817fff6be06b9b1f4ec3619b0b9ecc639bc681b73f646b"}, + {file = "pycares-4.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0322ad94bbaa7016139b5bbdcd0de6f6feb9d146d69e03a82aaca342e06830a6"}, + {file = "pycares-4.8.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:456c60f170c997f9a43c7afa1085fced8efb7e13ae49dd5656f998ae13c4bdb4"}, + {file = "pycares-4.8.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57a2c4c9ce423a85b0e0227409dbaf0d478f5e0c31d9e626768e77e1e887d32f"}, + {file = "pycares-4.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:478d9c479108b7527266864c0affe3d6e863492c9bc269217e36100c8fd89b91"}, + {file = "pycares-4.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:aed56bca096990ca0aa9bbf95761fc87e02880e04b0845922b5c12ea9abe523f"}, + {file = "pycares-4.8.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ef265a390928ee2f77f8901c2273c53293157860451ad453ce7f45dd268b72f9"}, + {file = "pycares-4.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a5f17d7a76d8335f1c90a8530c8f1e8bb22e9a1d70a96f686efaed946de1c908"}, + {file = "pycares-4.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:891f981feb2ef34367378f813fc17b3d706ce95b6548eeea0c9fe7705d7e54b1"}, + {file = "pycares-4.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4102f6d9117466cc0a1f527907a1454d109cc9e8551b8074888071ef16050fe3"}, + {file = "pycares-4.8.0-cp39-cp39-win32.whl", hash = "sha256:d6775308659652adc88c82c53eda59b5e86a154aaba5ad1e287bbb3e0be77076"}, + {file = "pycares-4.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:8bc05462aa44788d48544cca3d2532466fed2cdc5a2f24a43a92b620a61c9d19"}, + {file = "pycares-4.8.0.tar.gz", hash = "sha256:2fc2ebfab960f654b3e3cf08a732486950da99393a657f8b44618ad3ed2d39c1"}, +] + +[package.dependencies] +cffi = ">=1.5.0" + +[package.extras] +idna = ["idna (>=2.1)"] + +[[package]] +name = "pycep-parser" +version = "0.5.1" +description = "A Python based Bicep parser" +optional = false +python-versions = "<4.0,>=3.8" +groups = ["main"] +files = [ + {file = "pycep_parser-0.5.1-py3-none-any.whl", hash = "sha256:8c3f99c0dc1301193b1bcbe0a44c6b2763f6d2daf24964ca48dcdfbb73087fa0"}, + {file = "pycep_parser-0.5.1.tar.gz", hash = "sha256:683bb001077c09f98408285b1b6ba10cfb3941610966c45d0638a0e1a5e1d2a4"}, +] + +[package.dependencies] +lark = ">=1.1.2" +regex = ">=2022.1.18" +typing-extensions = ">=3.10.0" + [[package]] name = "pycodestyle" version = "2.12.1" @@ -3761,70 +4446,137 @@ markers = {dev = "platform_python_implementation != \"PyPy\""} [[package]] name = "pydantic" -version = "1.10.21" -description = "Data validation and settings management using python type hints" +version = "2.11.5" +description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" groups = ["main", "dev"] files = [ - {file = "pydantic-1.10.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:245e486e0fec53ec2366df9cf1cba36e0bbf066af7cd9c974bbbd9ba10e1e586"}, - {file = "pydantic-1.10.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c54f8d4c151c1de784c5b93dfbb872067e3414619e10e21e695f7bb84d1d1fd"}, - {file = "pydantic-1.10.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b64708009cfabd9c2211295144ff455ec7ceb4c4fb45a07a804309598f36187"}, - {file = "pydantic-1.10.21-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a148410fa0e971ba333358d11a6dea7b48e063de127c2b09ece9d1c1137dde4"}, - {file = "pydantic-1.10.21-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:36ceadef055af06e7756eb4b871cdc9e5a27bdc06a45c820cd94b443de019bbf"}, - {file = "pydantic-1.10.21-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0501e1d12df6ab1211b8cad52d2f7b2cd81f8e8e776d39aa5e71e2998d0379f"}, - {file = "pydantic-1.10.21-cp310-cp310-win_amd64.whl", hash = "sha256:c261127c275d7bce50b26b26c7d8427dcb5c4803e840e913f8d9df3f99dca55f"}, - {file = "pydantic-1.10.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8b6350b68566bb6b164fb06a3772e878887f3c857c46c0c534788081cb48adf4"}, - {file = "pydantic-1.10.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:935b19fdcde236f4fbf691959fa5c3e2b6951fff132964e869e57c70f2ad1ba3"}, - {file = "pydantic-1.10.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b6a04efdcd25486b27f24c1648d5adc1633ad8b4506d0e96e5367f075ed2e0b"}, - {file = "pydantic-1.10.21-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1ba253eb5af8d89864073e6ce8e6c8dec5f49920cff61f38f5c3383e38b1c9f"}, - {file = "pydantic-1.10.21-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:57f0101e6c97b411f287a0b7cf5ebc4e5d3b18254bf926f45a11615d29475793"}, - {file = "pydantic-1.10.21-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e85834f0370d737c77a386ce505c21b06bfe7086c1c568b70e15a568d9670d"}, - {file = "pydantic-1.10.21-cp311-cp311-win_amd64.whl", hash = "sha256:6a497bc66b3374b7d105763d1d3de76d949287bf28969bff4656206ab8a53aa9"}, - {file = "pydantic-1.10.21-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ed4a5f13cf160d64aa331ab9017af81f3481cd9fd0e49f1d707b57fe1b9f3ae"}, - {file = "pydantic-1.10.21-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b7693bb6ed3fbe250e222f9415abb73111bb09b73ab90d2d4d53f6390e0ccc1"}, - {file = "pydantic-1.10.21-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185d5f1dff1fead51766da9b2de4f3dc3b8fca39e59383c273f34a6ae254e3e2"}, - {file = "pydantic-1.10.21-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38e6d35cf7cd1727822c79e324fa0677e1a08c88a34f56695101f5ad4d5e20e5"}, - {file = "pydantic-1.10.21-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1d7c332685eafacb64a1a7645b409a166eb7537f23142d26895746f628a3149b"}, - {file = "pydantic-1.10.21-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c9b782db6f993a36092480eeaab8ba0609f786041b01f39c7c52252bda6d85f"}, - {file = "pydantic-1.10.21-cp312-cp312-win_amd64.whl", hash = "sha256:7ce64d23d4e71d9698492479505674c5c5b92cda02b07c91dfc13633b2eef805"}, - {file = "pydantic-1.10.21-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0067935d35044950be781933ab91b9a708eaff124bf860fa2f70aeb1c4be7212"}, - {file = "pydantic-1.10.21-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5e8148c2ce4894ce7e5a4925d9d3fdce429fb0e821b5a8783573f3611933a251"}, - {file = "pydantic-1.10.21-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4973232c98b9b44c78b1233693e5e1938add5af18042f031737e1214455f9b8"}, - {file = "pydantic-1.10.21-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:662bf5ce3c9b1cef32a32a2f4debe00d2f4839fefbebe1d6956e681122a9c839"}, - {file = "pydantic-1.10.21-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98737c3ab5a2f8a85f2326eebcd214510f898881a290a7939a45ec294743c875"}, - {file = "pydantic-1.10.21-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0bb58bbe65a43483d49f66b6c8474424d551a3fbe8a7796c42da314bac712738"}, - {file = "pydantic-1.10.21-cp313-cp313-win_amd64.whl", hash = "sha256:e622314542fb48542c09c7bd1ac51d71c5632dd3c92dc82ede6da233f55f4848"}, - {file = "pydantic-1.10.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d356aa5b18ef5a24d8081f5c5beb67c0a2a6ff2a953ee38d65a2aa96526b274f"}, - {file = "pydantic-1.10.21-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08caa8c0468172d27c669abfe9e7d96a8b1655ec0833753e117061febaaadef5"}, - {file = "pydantic-1.10.21-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c677aa39ec737fec932feb68e4a2abe142682f2885558402602cd9746a1c92e8"}, - {file = "pydantic-1.10.21-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:79577cc045d3442c4e845df53df9f9202546e2ba54954c057d253fc17cd16cb1"}, - {file = "pydantic-1.10.21-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:b6b73ab347284719f818acb14f7cd80696c6fdf1bd34feee1955d7a72d2e64ce"}, - {file = "pydantic-1.10.21-cp37-cp37m-win_amd64.whl", hash = "sha256:46cffa24891b06269e12f7e1ec50b73f0c9ab4ce71c2caa4ccf1fb36845e1ff7"}, - {file = "pydantic-1.10.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:298d6f765e3c9825dfa78f24c1efd29af91c3ab1b763e1fd26ae4d9e1749e5c8"}, - {file = "pydantic-1.10.21-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2f4a2305f15eff68f874766d982114ac89468f1c2c0b97640e719cf1a078374"}, - {file = "pydantic-1.10.21-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35b263b60c519354afb3a60107d20470dd5250b3ce54c08753f6975c406d949b"}, - {file = "pydantic-1.10.21-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e23a97a6c2f2db88995496db9387cd1727acdacc85835ba8619dce826c0b11a6"}, - {file = "pydantic-1.10.21-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:3c96fed246ccc1acb2df032ff642459e4ae18b315ecbab4d95c95cfa292e8517"}, - {file = "pydantic-1.10.21-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b92893ebefc0151474f682e7debb6ab38552ce56a90e39a8834734c81f37c8a9"}, - {file = "pydantic-1.10.21-cp38-cp38-win_amd64.whl", hash = "sha256:b8460bc256bf0de821839aea6794bb38a4c0fbd48f949ea51093f6edce0be459"}, - {file = "pydantic-1.10.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d387940f0f1a0adb3c44481aa379122d06df8486cc8f652a7b3b0caf08435f7"}, - {file = "pydantic-1.10.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:266ecfc384861d7b0b9c214788ddff75a2ea123aa756bcca6b2a1175edeca0fe"}, - {file = "pydantic-1.10.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61da798c05a06a362a2f8c5e3ff0341743e2818d0f530eaac0d6898f1b187f1f"}, - {file = "pydantic-1.10.21-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a621742da75ce272d64ea57bd7651ee2a115fa67c0f11d66d9dcfc18c2f1b106"}, - {file = "pydantic-1.10.21-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9e3e4000cd54ef455694b8be9111ea20f66a686fc155feda1ecacf2322b115da"}, - {file = "pydantic-1.10.21-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f198c8206640f4c0ef5a76b779241efb1380a300d88b1bce9bfe95a6362e674d"}, - {file = "pydantic-1.10.21-cp39-cp39-win_amd64.whl", hash = "sha256:e7f0cda108b36a30c8fc882e4fc5b7eec8ef584aa43aa43694c6a7b274fb2b56"}, - {file = "pydantic-1.10.21-py3-none-any.whl", hash = "sha256:db70c920cba9d05c69ad4a9e7f8e9e83011abb2c6490e561de9ae24aee44925c"}, - {file = "pydantic-1.10.21.tar.gz", hash = "sha256:64b48e2b609a6c22178a56c408ee1215a7206077ecb8a193e2fda31858b2362a"}, + {file = "pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7"}, + {file = "pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a"}, ] [package.dependencies] -typing-extensions = ">=4.2.0" +annotated-types = ">=0.6.0" +pydantic-core = "2.33.2" +typing-extensions = ">=4.12.2" +typing-inspection = ">=0.4.0" [package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, + {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pyflakes" @@ -3986,6 +4738,61 @@ files = [ [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "pyston" +version = "2.3.5" +description = "A JIT for Python" +optional = false +python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.10\" and (sys_platform == \"linux\" or sys_platform == \"darwin\") and platform_machine == \"x86_64\" and implementation_name == \"cpython\"" +files = [ + {file = "pyston-2.3.5-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:deb9dac7f8f67d1b2dc709300e1d8fda51bbc1375957509f58e1dc4459324ea7"}, + {file = "pyston-2.3.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d33602480ff742a45e21413377123bcead27c5ea11b06efaf0260ccb60633da3"}, + {file = "pyston-2.3.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b93ddaed1e62b8bd261e531f3356590014822f7451619000c6b9efe699dd148f"}, + {file = "pyston-2.3.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30caaee3b58d92817efa2cd4f32c24289dd5f4ddf9b5b4ec5b62ed564230ca8a"}, + {file = "pyston-2.3.5-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:5ea981d286de250467e48fd8d80d461acd1e4f27cd10775478206b273045c58d"}, + {file = "pyston-2.3.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44211f95ba99f4d6bd5fc5e27aba834644ba0277554fc52f9a98672720c3ff17"}, + {file = "pyston-2.3.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3805cac00fde2791408d09a988b32911009dcd86a8215a17d9a85dd83fe1c662"}, + {file = "pyston-2.3.5-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cab90f5bf4646c6de85c25763762dcfa94d209de955173b3f83e3c108d39028f"}, + {file = "pyston-2.3.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c2bddc7ea4755476ec9b75af94a63346ff6d2f34225dff73bb48c8a9f38795da"}, + {file = "pyston-2.3.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf36e6bdb84417b3291052e9156e8dfb03c3ec4879973bf7a47253fef24506d7"}, + {file = "pyston-2.3.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:539ad38ecd392cf60586122db2af45c22fc01fd83dc466ef05e3de7cfb79adb2"}, + {file = "pyston-2.3.5-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:5872e66a4583d56d9555caf6fa1959a33437593a75bf22c982e535d9e742dd38"}, + {file = "pyston-2.3.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a64edbcbf9494ee0e0c230544929d274a2705798abf0e298d82317dbfa5449e9"}, + {file = "pyston-2.3.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1f88d0594edc40b7d7fc4880e0aef33a69c97b4af0b14c91e8b5f4847ac618"}, + {file = "pyston-2.3.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f983b89f0f79ee527f2cadf167bc8c72b3e1c40574f71f12717a4cb13c75ed47"}, +] + +[[package]] +name = "pyston-autoload" +version = "2.3.5" +description = "Automatically loads and enables pyston" +optional = false +python-versions = "*" +groups = ["main"] +markers = "python_version <= \"3.10\" and (sys_platform == \"linux\" or sys_platform == \"darwin\") and platform_machine == \"x86_64\" and implementation_name == \"cpython\"" +files = [ + {file = "pyston_autoload-2.3.5-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:50c5d2e2855a542f9e427601ed1cc94aa1ff82937c001e5765f4db67af8a309a"}, + {file = "pyston_autoload-2.3.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2efd392dffb78f782ccb40775086f7c53c7ad85fb469f6dddfc29141da37df26"}, + {file = "pyston_autoload-2.3.5-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d4c9f5624087b66713e5e674e055a77492ac1c19c40dfc306053c5eb7d970a98"}, + {file = "pyston_autoload-2.3.5-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:14d09effb82c436b2b090cf3293e8279e14af065ed5383ab06f72d5481f89cfd"}, + {file = "pyston_autoload-2.3.5-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:fbedb3013c93d52959c543bc9493572401d8f8e928bc265af6fdf6a2fb0258e8"}, + {file = "pyston_autoload-2.3.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:4321d6d12ac04613d8f3cac4ff2c07d01a2c6736dc0ecf587808c75f755173df"}, + {file = "pyston_autoload-2.3.5-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:adf3fa86cfaf9968df7c22260bf63f27139a268dfa5fa01d23a0ae3e5ede92db"}, + {file = "pyston_autoload-2.3.5-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:940734109bd38beca8b0211c540492b2888bada4e332415906ba768002cb45b1"}, + {file = "pyston_autoload-2.3.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26e7769005b4b33b48e51d822a3e3f81e5c2ce530634c0e614e7a939016a2171"}, + {file = "pyston_autoload-2.3.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d9eed4629ae2c798dff581b30a5044e369d2a0d7a8d19754dc95f48cd532fb97"}, + {file = "pyston_autoload-2.3.5-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:9ce3e3bdfbbb7b5900600cc6d1dcd68dfca145a61d10250263e1a4537ab4ff58"}, + {file = "pyston_autoload-2.3.5-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:4fd03b7cd2b439edda14ce7dd6e97158d0f75ff21f270a0c3792cd8341dcf0fa"}, + {file = "pyston_autoload-2.3.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd6b85b50d3a86caec0db5382550abefe94fd1341dea8014cc7ba6d69daf6c86"}, + {file = "pyston_autoload-2.3.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f2c191a1cbcee2ed70d65510dd540edb5d5d2b3288b74be89be401456ae747d1"}, + {file = "pyston_autoload-2.3.5-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:7533445844ec987c2c10983485a0758dbbbfe5bdc98264a6ff4a70cdf6d1df74"}, +] + +[package.dependencies] +pyston = "2.3.5" + [[package]] name = "pytest" version = "8.3.5" @@ -4117,7 +4924,7 @@ version = "310" description = "Python for Window Extensions" optional = false python-versions = "*" -groups = ["dev"] +groups = ["main", "dev"] markers = "sys_platform == \"win32\"" files = [ {file = "pywin32-310-cp310-cp310-win32.whl", hash = "sha256:6dd97011efc8bf51d6793a82292419eba2c71cf8e7250cfac03bba284454abc1"}, @@ -4216,6 +5023,29 @@ files = [ [package.dependencies] pyyaml = "*" +[[package]] +name = "rdflib" +version = "7.1.4" +description = "RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information." +optional = false +python-versions = "<4.0.0,>=3.8.1" +groups = ["main"] +files = [ + {file = "rdflib-7.1.4-py3-none-any.whl", hash = "sha256:72f4adb1990fa5241abd22ddaf36d7cafa5d91d9ff2ba13f3086d339b213d997"}, + {file = "rdflib-7.1.4.tar.gz", hash = "sha256:fed46e24f26a788e2ab8e445f7077f00edcf95abb73bcef4b86cefa8b62dd174"}, +] + +[package.dependencies] +isodate = {version = ">=0.7.2,<1.0.0", markers = "python_version < \"3.11\""} +pyparsing = ">=2.1.0,<4" + +[package.extras] +berkeleydb = ["berkeleydb (>=18.1.0,<19.0.0)"] +html = ["html5rdf (>=1.2,<2)"] +lxml = ["lxml (>=4.3,<6.0)"] +networkx = ["networkx (>=2,<4)"] +orjson = ["orjson (>=3.9.14,<4)"] + [[package]] name = "referencing" version = "0.36.2" @@ -4239,7 +5069,7 @@ version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" -groups = ["dev", "docs"] +groups = ["main", "dev", "docs"] files = [ {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, @@ -4673,6 +5503,35 @@ files = [ {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, ] +[[package]] +name = "rustworkx" +version = "0.16.0" +description = "A python graph library implemented in Rust" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "rustworkx-0.16.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:476a6c67b0142acd941691943750cc6737a48372304489969c2b62d30aaf4c27"}, + {file = "rustworkx-0.16.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bef2ef42870f806af93979b457e240f6dfa4f867ca33965c620f3a804409ed3a"}, + {file = "rustworkx-0.16.0-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0db3a73bf68b3e66c08322a2fc95d3aa663d037d9b4e49c3509da4898d3529cc"}, + {file = "rustworkx-0.16.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f12a13d7486234fa2a84746d5e41f436bf9df43548043e7a232f48804ff8c61"}, + {file = "rustworkx-0.16.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89efd5c3a4653ddacc55ca39f28b261d43deec7d678f8f8fc6b76b5087f1dfea"}, + {file = "rustworkx-0.16.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec0c12aac8c54910ace20ac6ada4b890cd39f95f69100514715f8ad7af9041e4"}, + {file = "rustworkx-0.16.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d650e39fc1a1534335f7517358ebfc3478bb235428463cfcd7c5750d50377b33"}, + {file = "rustworkx-0.16.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:293180b83509ee9bff4c3af7ccc1024f6528d61b65d0cb7320bd31924f10cb71"}, + {file = "rustworkx-0.16.0-cp39-abi3-win32.whl", hash = "sha256:040c4368729cf502f756a3b0ff5f1c6915fc389f74dcc6afc6c3833688c97c01"}, + {file = "rustworkx-0.16.0-cp39-abi3-win_amd64.whl", hash = "sha256:905df608843c32fa45ac023687769fe13056edf7584474c801d5c50705d76e9b"}, + {file = "rustworkx-0.16.0.tar.gz", hash = "sha256:9f0dcb83f38d5ca2c3a683eb9b6951c8aec3262fbfe5141946a7ee5ba37e0bb6"}, +] + +[package.dependencies] +numpy = ">=1.16.0,<3" + +[package.extras] +all = ["matplotlib (>=3.0)", "pillow (>=5.4)"] +graphviz = ["pillow (>=5.4)"] +mpl = ["matplotlib (>=3.0)"] + [[package]] name = "s3transfer" version = "0.10.4" @@ -4748,16 +5607,35 @@ typing-extensions = ">=4.7.1" [[package]] name = "schema" -version = "0.7.7" +version = "0.7.5" description = "Simple data validation library" optional = false python-versions = "*" groups = ["main"] files = [ - {file = "schema-0.7.7-py2.py3-none-any.whl", hash = "sha256:5d976a5b50f36e74e2157b47097b60002bd4d42e65425fcc9c9befadb4255dde"}, - {file = "schema-0.7.7.tar.gz", hash = "sha256:7da553abd2958a19dc2547c388cde53398b39196175a9be59ea1caf5ab0a1807"}, + {file = "schema-0.7.5-py2.py3-none-any.whl", hash = "sha256:f3ffdeeada09ec34bf40d7d79996d9f7175db93b7a5065de0faa7f41083c1e6c"}, + {file = "schema-0.7.5.tar.gz", hash = "sha256:f06717112c61895cabc4707752b88716e8420a8819d71404501e114f91043197"}, ] +[package.dependencies] +contextlib2 = ">=0.5.5" + +[[package]] +name = "semantic-version" +version = "2.10.0" +description = "A library implementing the 'SemVer' scheme." +optional = false +python-versions = ">=2.7" +groups = ["main"] +files = [ + {file = "semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177"}, + {file = "semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c"}, +] + +[package.extras] +dev = ["Django (>=1.11)", "check-manifest", "colorama (<=0.4.1) ; python_version == \"3.4\"", "coverage", "flake8", "nose2", "readme-renderer (<25.0) ; python_version == \"3.4\"", "tox", "wheel", "zest.releaser[recommended]"] +doc = ["Sphinx", "sphinx-rtd-theme"] + [[package]] name = "setuptools" version = "79.0.0" @@ -4843,7 +5721,7 @@ version = "5.0.2" description = "A pure Python implementation of a sliding window memory map manager" optional = false python-versions = ">=3.7" -groups = ["docs"] +groups = ["main", "docs"] files = [ {file = "smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e"}, {file = "smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5"}, @@ -4861,6 +5739,59 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "sortedcontainers" +version = "2.4.0" +description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, + {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, +] + +[[package]] +name = "soupsieve" +version = "2.7" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, + {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, +] + +[[package]] +name = "spdx-tools" +version = "0.8.3" +description = "SPDX parser and tools." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "spdx-tools-0.8.3.tar.gz", hash = "sha256:68b8f9ce2893b5216bd90b2e63f1c821c2884e4ebc4fd295ebbf1fa8b8a94b93"}, + {file = "spdx_tools-0.8.3-py3-none-any.whl", hash = "sha256:638fd9bd8be61901316eb6d063574e16d5403a1870073ec4d9241426a997501a"}, +] + +[package.dependencies] +beartype = "*" +click = "*" +license-expression = "*" +ply = "*" +pyyaml = "*" +rdflib = "*" +semantic-version = "*" +uritools = "*" +xmltodict = "*" + +[package.extras] +code-style = ["black", "flake8", "isort"] +development = ["black", "flake8", "isort", "networkx", "pytest"] +graph-generation = ["networkx", "pygraphviz"] +test = ["pyshacl", "pytest", "tzdata"] + [[package]] name = "std-uritemplate" version = "2.0.3" @@ -4921,6 +5852,21 @@ files = [ [package.extras] widechars = ["wcwidth"] +[[package]] +name = "termcolor" +version = "2.3.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "termcolor-2.3.0-py3-none-any.whl", hash = "sha256:3afb05607b89aed0ffe25202399ee0867ad4d3cb4180d98aaf8eefa6a5f7d475"}, + {file = "termcolor-2.3.0.tar.gz", hash = "sha256:b5b08f68937f138fe92f6c089b99f1e2da0ae56c52b78bf7075fd95420fd9a5a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + [[package]] name = "tldextract" version = "5.3.0" @@ -4998,6 +5944,28 @@ files = [ {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, ] +[[package]] +name = "tqdm" +version = "4.67.1" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + [[package]] name = "typer" version = "0.15.2" @@ -5028,6 +5996,21 @@ files = [ {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] +[[package]] +name = "typing-inspection" +version = "0.4.1" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, + {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + [[package]] name = "tzdata" version = "2025.2" @@ -5058,6 +6041,18 @@ tzdata = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] +[[package]] +name = "unidiff" +version = "0.7.5" +description = "Unified diff parsing/metadata extraction library." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "unidiff-0.7.5-py2.py3-none-any.whl", hash = "sha256:c93bf2265cc1ba2a520e415ab05da587370bc2a3ae9e0414329f54f0c2fc09e8"}, + {file = "unidiff-0.7.5.tar.gz", hash = "sha256:2e5f0162052248946b9f0970a40e9e124236bf86c82b70821143a6fc1dea2574"}, +] + [[package]] name = "uritemplate" version = "4.1.1" @@ -5070,6 +6065,18 @@ files = [ {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, ] +[[package]] +name = "uritools" +version = "5.0.0" +description = "URI parsing, classification and composition" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "uritools-5.0.0-py3-none-any.whl", hash = "sha256:cead3a49ba8fbca3f91857343849d506d8639718f4a2e51b62e87393b493bd6f"}, + {file = "uritools-5.0.0.tar.gz", hash = "sha256:68180cad154062bd5b5d9ffcdd464f8de6934414b25462ae807b00b8df9345de"}, +] + [[package]] name = "urllib3" version = "1.26.20" @@ -5165,6 +6172,18 @@ files = [ [package.extras] watchmedo = ["PyYAML (>=3.10)"] +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + [[package]] name = "websocket-client" version = "1.8.0" @@ -5307,7 +6326,7 @@ version = "0.14.2" description = "Makes working with XML feel like you are working with JSON" optional = false python-versions = ">=3.6" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac"}, {file = "xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553"}, @@ -5456,4 +6475,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">3.9.1,<3.13" -content-hash = "f504af1d00a1da9dd65269509daf32f919c13be6c46f25cd9ef9bfba6b9c9a07" +content-hash = "a07dc0d6c4c1b1533e527bb0c2da2162de5fe37ce96b11ab367b0bb3126a5a45" diff --git a/prowler/__main__.py b/prowler/__main__.py index e22d7b7fa6..0cc78c7915 100644 --- a/prowler/__main__.py +++ b/prowler/__main__.py @@ -98,6 +98,7 @@ from prowler.providers.common.provider import Provider from prowler.providers.common.quick_inventory import run_provider_quick_inventory from prowler.providers.gcp.models import GCPOutputOptions from prowler.providers.github.models import GithubOutputOptions +from prowler.providers.iac.models import IACOutputOptions from prowler.providers.kubernetes.models import KubernetesOutputOptions from prowler.providers.m365.models import M365OutputOptions from prowler.providers.nhn.models import NHNOutputOptions @@ -175,11 +176,13 @@ def prowler(): # Load compliance frameworks logger.debug("Loading compliance frameworks from .json files") - bulk_compliance_frameworks = Compliance.get_bulk(provider) - # Complete checks metadata with the compliance framework specification - bulk_checks_metadata = update_checks_metadata_with_compliance( - bulk_compliance_frameworks, bulk_checks_metadata - ) + # Skip compliance frameworks for IAC provider + if provider != "iac": + bulk_compliance_frameworks = Compliance.get_bulk(provider) + # Complete checks metadata with the compliance framework specification + bulk_checks_metadata = update_checks_metadata_with_compliance( + bulk_compliance_frameworks, bulk_checks_metadata + ) # Update checks metadata if the --custom-checks-metadata-file is present custom_checks_metadata = None @@ -231,39 +234,45 @@ def prowler(): if not args.only_logs: global_provider.print_credentials() - # Import custom checks from folder - if checks_folder: - custom_checks = parse_checks_from_folder(global_provider, checks_folder) - # Workaround to be able to execute custom checks alongside all checks if nothing is explicitly set - if ( - not checks_file - and not checks - and not services - and not severities - and not compliance_framework - and not categories - ): - checks_to_execute.update(custom_checks) + # Skip service and check loading for IAC provider + if provider != "iac": + # Import custom checks from folder + if checks_folder: + custom_checks = parse_checks_from_folder(global_provider, checks_folder) + # Workaround to be able to execute custom checks alongside all checks if nothing is explicitly set + if ( + not checks_file + and not checks + and not services + and not severities + and not compliance_framework + and not categories + ): + checks_to_execute.update(custom_checks) - # Exclude checks if -e/--excluded-checks - if excluded_checks: - checks_to_execute = exclude_checks_to_run(checks_to_execute, excluded_checks) + # Exclude checks if -e/--excluded-checks + if excluded_checks: + checks_to_execute = exclude_checks_to_run( + checks_to_execute, excluded_checks + ) - # Exclude services if --excluded-services - if excluded_services: - checks_to_execute = exclude_services_to_run( - checks_to_execute, excluded_services, provider + # Exclude services if --excluded-services + if excluded_services: + checks_to_execute = exclude_services_to_run( + checks_to_execute, excluded_services, provider + ) + + # Once the provider is set and we have the eventual checks based on the resource identifier, + # it is time to check what Prowler's checks are going to be executed + checks_from_resources = ( + global_provider.get_checks_to_execute_by_audit_resources() ) + # Intersect checks from resources with checks to execute so we only run the checks that apply to the resources with the specified ARNs or tags + if getattr(args, "resource_arn", None) or getattr(args, "resource_tag", None): + checks_to_execute = checks_to_execute.intersection(checks_from_resources) - # Once the provider is set and we have the eventual checks based on the resource identifier, - # it is time to check what Prowler's checks are going to be executed - checks_from_resources = global_provider.get_checks_to_execute_by_audit_resources() - # Intersect checks from resources with checks to execute so we only run the checks that apply to the resources with the specified ARNs or tags - if getattr(args, "resource_arn", None) or getattr(args, "resource_tag", None): - checks_to_execute = checks_to_execute.intersection(checks_from_resources) - - # Sort final check list - checks_to_execute = sorted(checks_to_execute) + # Sort final check list + checks_to_execute = sorted(checks_to_execute) # Setup Output Options if provider == "aws": @@ -294,6 +303,8 @@ def prowler(): output_options = NHNOutputOptions( args, bulk_checks_metadata, global_provider.identity ) + elif provider == "iac": + output_options = IACOutputOptions(args, bulk_checks_metadata) # Run the quick inventory for the provider if available if hasattr(args, "quick_inventory") and args.quick_inventory: @@ -303,7 +314,10 @@ def prowler(): # Execute checks findings = [] - if len(checks_to_execute): + if provider == "iac": + # For IAC provider, run the scan directly + findings = global_provider.run() + elif len(checks_to_execute): findings = execute_checks( checks_to_execute, global_provider, diff --git a/prowler/compliance/iac/__init__.py b/prowler/compliance/iac/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/config/config.py b/prowler/config/config.py index de54add886..dbbe71db9e 100644 --- a/prowler/config/config.py +++ b/prowler/config/config.py @@ -30,6 +30,7 @@ class Provider(str, Enum): KUBERNETES = "kubernetes" M365 = "m365" GITHUB = "github" + IAC = "iac" NHN = "nhn" diff --git a/prowler/lib/check/checks_loader.py b/prowler/lib/check/checks_loader.py index f75e9672b0..f45b0e917d 100644 --- a/prowler/lib/check/checks_loader.py +++ b/prowler/lib/check/checks_loader.py @@ -20,6 +20,10 @@ def load_checks_to_execute( ) -> set: """Generate the list of checks to execute based on the cloud provider and the input arguments given""" try: + # Bypass check loading for IAC provider since it uses Checkov directly + if provider == "iac": + return set() + # Local subsets checks_to_execute = set() check_aliases = {} diff --git a/prowler/lib/check/compliance_models.py b/prowler/lib/check/compliance_models.py index 5e01e590f3..428bd85ad1 100644 --- a/prowler/lib/check/compliance_models.py +++ b/prowler/lib/check/compliance_models.py @@ -80,6 +80,7 @@ class CIS_Requirement_Attribute_AssessmentStatus(str): # CIS Requirement Attribute class CIS_Requirement_Attribute(BaseModel): + model_config = {"arbitrary_types_allowed": True} """CIS Requirement Attribute""" Section: str diff --git a/prowler/lib/check/models.py b/prowler/lib/check/models.py index 875350c757..b364da4810 100644 --- a/prowler/lib/check/models.py +++ b/prowler/lib/check/models.py @@ -608,6 +608,42 @@ class CheckReportM365(Check_Report): self.location = resource_location +@dataclass +class CheckReportIAC(Check_Report): + """Contains the IAC Check's finding information using Checkov.""" + + metadata: dict + check_id: str + check_name: str + check_result: dict + check_result_status: str + file_path: str + file_line_range: list + guideline: str + resource: str + severity: str + + def __init__(self, metadata: dict = {}, finding: dict = {}) -> None: + """ + Initialize the IAC Check's finding information from a Checkov failed_check dict. + + Args: + metadata (Dict): Optional check metadata (can be None). + failed_check (dict): A single failed_check result from Checkov's JSON output. + """ + super().__init__(metadata, finding) + + self.check_id = finding.get("check_id", "") + self.check_name = finding.get("check_name", "") + self.check_result = finding.get("check_result", {}) + self.check_result_status = self.check_result.get("result", "UNKNOWN") + self.file_path = finding.get("file_path", "") + self.file_line_range = finding.get("file_line_range", []) + self.guideline = finding.get("guideline", "") + self.resource = finding.get("resource", "") + self.severity = finding.get("severity", "UNKNOWN") + + @dataclass class CheckReportNHN(Check_Report): """Contains the NHN Check's finding information.""" diff --git a/prowler/lib/check/utils.py b/prowler/lib/check/utils.py index c9e6d6de00..bf8854600d 100644 --- a/prowler/lib/check/utils.py +++ b/prowler/lib/check/utils.py @@ -14,6 +14,10 @@ def recover_checks_from_provider( Returns a list of tuples with the following format (check_name, check_path) """ try: + # Bypass check loading for IAC provider since it uses Checkov directly + if provider == "iac": + return [] + checks = [] modules = list_modules(provider, service) for module_name in modules: @@ -59,6 +63,10 @@ def recover_checks_from_service(service_list: list, provider: str) -> set: Returns a set of checks from the given services """ try: + # Bypass check loading for IAC provider since it uses Checkov directly + if provider == "iac": + return set() + checks = set() service_list = [ "awslambda" if service == "lambda" else service for service in service_list diff --git a/prowler/lib/cli/parser.py b/prowler/lib/cli/parser.py index 1990c7f90a..9a68d5ab44 100644 --- a/prowler/lib/cli/parser.py +++ b/prowler/lib/cli/parser.py @@ -26,16 +26,17 @@ class ProwlerArgumentParser: self.parser = argparse.ArgumentParser( prog="prowler", formatter_class=RawTextHelpFormatter, - usage="prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,nhn,dashboard} ...", + usage="prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,nhn,dashboard,iac} ...", epilog=""" Available Cloud Providers: - {aws,azure,gcp,kubernetes,m365,nhn} + {aws,azure,gcp,kubernetes,m365,nhn,iac} aws AWS Provider azure Azure Provider gcp GCP Provider kubernetes Kubernetes Provider github GitHub Provider m365 Microsoft 365 Provider + iac IaC Provider nhn NHN Provider (Unofficial) Available components: diff --git a/prowler/lib/outputs/finding.py b/prowler/lib/outputs/finding.py index ffa0a2f44e..428637e4f3 100644 --- a/prowler/lib/outputs/finding.py +++ b/prowler/lib/outputs/finding.py @@ -282,6 +282,14 @@ class Finding(BaseModel): output_data["resource_uid"] = check_output.resource_id output_data["region"] = check_output.location + elif provider.type == "iac": + output_data["auth_method"] = "iac" + output_data["account_uid"] = "iac" + output_data["account_name"] = "iac" + output_data["resource_name"] = check_output.check_name + output_data["resource_uid"] = check_output.check_id + output_data["region"] = check_output.file_path + # check_output Unique ID # TODO: move this to a function # TODO: in Azure, GCP and K8s there are findings without resource_name diff --git a/prowler/lib/outputs/summary_table.py b/prowler/lib/outputs/summary_table.py index b1d9c7b8c1..fadceea23e 100644 --- a/prowler/lib/outputs/summary_table.py +++ b/prowler/lib/outputs/summary_table.py @@ -54,6 +54,9 @@ def display_summary_table( elif provider.type == "nhn": entity_type = "Tenant Domain" audited_entities = provider.identity.tenant_domain + elif provider.type == "iac": + entity_type = "Directory" + audited_entities = provider.scan_path # Check if there are findings and that they are not all MANUAL if findings and not all(finding.status == "MANUAL" for finding in findings): diff --git a/prowler/providers/common/models.py b/prowler/providers/common/models.py index 47c9ebd2fe..bac7f29943 100644 --- a/prowler/providers/common/models.py +++ b/prowler/providers/common/models.py @@ -9,6 +9,10 @@ from prowler.providers.common.provider import Provider # TODO: include this for all the providers class Audit_Metadata(BaseModel): + provider: str + account_id: str + account_name: str + region: str services_scanned: int # We can't use a set in the expected # checks because the set is unordered diff --git a/prowler/providers/common/provider.py b/prowler/providers/common/provider.py index 9c97d63fbc..4d950e49d6 100644 --- a/prowler/providers/common/provider.py +++ b/prowler/providers/common/provider.py @@ -149,7 +149,11 @@ class Provider(ABC): provider_class_path = ( f"{providers_path}.{arguments.provider}.{arguments.provider}_provider" ) - provider_class_name = f"{arguments.provider.capitalize()}Provider" + # Special case for IAC provider + if arguments.provider == "iac": + provider_class_name = "IACProvider" + else: + provider_class_name = f"{arguments.provider.capitalize()}Provider" provider_class = getattr( import_module(provider_class_path), provider_class_name ) @@ -243,6 +247,14 @@ class Provider(ABC): mutelist_path=arguments.mutelist_file, config_path=arguments.config_file, ) + elif "iac" in provider_class_name.lower(): + provider = provider_class( + scan_path=arguments.scan_path, + config_path=arguments.config_file, + mutelist_path=arguments.mutelist_file, + fixer_config=fixer_config, + ) + Provider.set_global_provider(provider) except TypeError as error: logger.critical( diff --git a/prowler/providers/iac/__init__.py b/prowler/providers/iac/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/iac/iac.py b/prowler/providers/iac/iac.py new file mode 100644 index 0000000000..4307360a9f --- /dev/null +++ b/prowler/providers/iac/iac.py @@ -0,0 +1,3 @@ +from prowler.providers.iac.iac_provider import IACProvider + +provider = IACProvider(scan_path=globals().get("args", None).scan_path) diff --git a/prowler/providers/iac/iac_provider.py b/prowler/providers/iac/iac_provider.py new file mode 100644 index 0000000000..62d5f13c92 --- /dev/null +++ b/prowler/providers/iac/iac_provider.py @@ -0,0 +1,166 @@ +import json +import subprocess +import sys +from typing import List + +from colorama import Fore, Style + +from prowler.lib.check.models import CheckReportIAC +from prowler.lib.logger import logger +from prowler.lib.utils.utils import print_boxes +from prowler.providers.common.models import Audit_Metadata +from prowler.providers.common.provider import Provider + + +class IACProvider(Provider): + _type: str = "iac" + audit_metadata: Audit_Metadata + + def __init__( + self, + scan_path: str = ".", + config_path: str = None, + config_content: dict = None, + fixer_config: dict = {}, + mutelist_path: str = None, + mutelist_content: dict = None, + ): + logger.info("Instantiating IAC Provider...") + + self.scan_path = scan_path + self.region = "global" + self.audited_account = "local-iac" + self._session = None + self._identity = "prowler" + + self._audit_config = config_content if config_content else {} + self._fixer_config = fixer_config + self._mutelist = None + + self.audit_metadata = Audit_Metadata( + provider=self._type, + account_id=self.audited_account, + account_name="iac", + region=self.region, + services_scanned=0, # IAC doesn't use services + expected_checks=[], # IAC doesn't use checks + completed_checks=0, # IAC doesn't use checks + audit_progress=0, # IAC doesn't use progress tracking + ) + + Provider.set_global_provider(self) + + @property + def type(self): + return self._type + + @property + def identity(self): + return self._identity + + @property + def session(self): + return self._session + + @property + def audit_config(self): + return self._audit_config + + @property + def fixer_config(self): + return self._fixer_config + + def setup_session(self): + """IAC provider doesn't need a session since it uses Checkov directly""" + return None + + def run(self) -> List[CheckReportIAC]: + return self.run_scan(self.scan_path) + + def run_scan(self, directory: str) -> List[CheckReportIAC]: + try: + logger.info(f"Running IaC scan on {directory}...") + + # Run Checkov with JSON output + process = subprocess.run( + ["checkov", "-d", directory, "-o", "json"], + capture_output=True, + text=True, + ) + + try: + output = json.loads(process.stdout) + except json.JSONDecodeError: + logger.error("Failed to parse IaC scan output as JSON") + return [] + + reports = [] + + # Process failed checks + for finding in output.get("results", {}).get("failed_checks", []): + # Get severity and ensure it's a valid string + severity = finding.get("severity", "low") + if severity is None: + severity = "low" + severity = str(severity).lower() + + # Create a basic metadata structure for the check + metadata_dict = { + "Provider": "iac", + "CheckID": finding.get("check_id", ""), + "CheckTitle": finding.get("check_name", ""), + "CheckType": ["Infrastructure as Code"], + "ServiceName": "iac", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": severity, + "ResourceType": "iac", + "Description": finding.get("check_name", ""), + "Risk": "", + "RelatedUrl": finding.get("guideline", ""), + "Remediation": { + "Code": { + "NativeIaC": "", + "Terraform": "", + "CLI": "", + "Other": "", + }, + "Recommendation": { + "Text": "", + "Url": finding.get("guideline", ""), + }, + }, + "Categories": ["security"], + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + } + + # Convert metadata dict to JSON string + metadata = json.dumps(metadata_dict) + + report = CheckReportIAC(metadata=metadata, finding=finding) + reports.append(report) + + # Log summary + summary = output.get("summary", {}) + logger.info( + f"IaC scan completed with {summary.get('passed', 0)} passed, " + f"{summary.get('failed', 0)} failed, and " + f"{summary.get('skipped', 0)} skipped checks." + ) + + return reports + + except Exception as error: + logger.critical( + f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}" + ) + sys.exit(1) + + def print_credentials(self): + report_lines = [ + f"Directory: {Fore.YELLOW}{self.scan_path}{Style.RESET_ALL}", + ] + report_title = f"{Style.BRIGHT}Scanning local IaC directory:{Style.RESET_ALL}" + print_boxes(report_lines, report_title) diff --git a/prowler/providers/iac/lib/__init__.py b/prowler/providers/iac/lib/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/iac/lib/arguments/__init__.py b/prowler/providers/iac/lib/arguments/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/iac/lib/arguments/arguments.py b/prowler/providers/iac/lib/arguments/arguments.py new file mode 100644 index 0000000000..d02d476059 --- /dev/null +++ b/prowler/providers/iac/lib/arguments/arguments.py @@ -0,0 +1,12 @@ +def init_parser(self): + """Init the IAC Provider CLI parser""" + iac_parser = self.subparsers.add_parser( + "iac", parents=[self.common_providers_parser], help="IaC Provider" + ) + iac_parser.add_argument( + "--scan-path", + "-P", + dest="scan_path", + default=".", + help="Path to the folder containing your infrastructure-as-code files. Default: current directory", + ) diff --git a/prowler/providers/iac/models.py b/prowler/providers/iac/models.py new file mode 100644 index 0000000000..3c9c04f849 --- /dev/null +++ b/prowler/providers/iac/models.py @@ -0,0 +1,27 @@ +from prowler.config.config import output_file_timestamp +from prowler.providers.common.models import ProviderOutputOptions + + +class IACOutputOptions(ProviderOutputOptions): + """ + IACOutputOptions overrides ProviderOutputOptions for IAC-specific output logic. + For example, generating a filename that includes the IAC tenant_id. + + Attributes inherited from ProviderOutputOptions: + - output_filename (str): The base filename used for generated reports. + - output_directory (str): The directory to store the output files. + - ... see ProviderOutputOptions for more details. + + Methods: + - __init__: Customizes the output filename logic for IAC. + """ + + def __init__(self, arguments, bulk_checks_metadata): + super().__init__(arguments, bulk_checks_metadata) + + # If --output-filename is not specified, build a default name. + if not getattr(arguments, "output_filename", None): + self.output_filename = f"prowler-output-iac-{output_file_timestamp}" + # If --output-filename was explicitly given, respect that + else: + self.output_filename = arguments.output_filename diff --git a/pyproject.toml b/pyproject.toml index 26cc8d967e..fffa49c0ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "azure-mgmt-subscription==3.1.1", "azure-mgmt-web==8.0.0", "azure-storage-blob==12.24.1", - "boto3==1.35.99", + "boto3 (==1.35.49)", "botocore==1.35.99", "colorama==0.4.6", "cryptography==44.0.1", @@ -48,16 +48,17 @@ dependencies = [ "msgraph-sdk==1.23.0", "numpy==2.0.2", "pandas==2.2.3", - "py-ocsf-models==0.3.1", - "pydantic==1.10.21", + "py-ocsf-models==0.5.0", + "pydantic (>=2.0,<3.0)", "pygithub==2.5.0", "python-dateutil (>=2.9.0.post0,<3.0.0)", "pytz==2025.1", - "schema==0.7.7", + "schema (==0.7.5)", "shodan==1.31.0", "slack-sdk==3.34.0", "tabulate==0.9.0", - "tzlocal==5.3.1" + "tzlocal==5.3.1", + "checkov (>=3.2.434,<4.0.0)" ] description = "Prowler is an Open Source security tool to perform AWS, GCP and Azure security best practices assessments, audits, incident response, continuous monitoring, hardening and forensics readiness. It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, FedRAMP, PCI-DSS, GDPR, HIPAA, FFIEC, SOC2, GXP, AWS Well-Architected Framework Security Pillar, AWS Foundational Technical Review (FTR), ENS (Spanish National Security Scheme) and your custom security frameworks." license = "Apache-2.0" From 290a54ce21f332f6025d17cc12c0d6414a8eb2f8 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Tue, 27 May 2025 16:20:48 +0200 Subject: [PATCH 02/24] fix: remove unnecessary file --- iac/main.tf | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 iac/main.tf diff --git a/iac/main.tf b/iac/main.tf deleted file mode 100644 index 9b41a9fb3c..0000000000 --- a/iac/main.tf +++ /dev/null @@ -1,4 +0,0 @@ -resource "aws_s3_bucket" "bad_example" { - bucket = "my-unsafe-bucket" - acl = "public-read" -} From a75452df03913a1070d1094c64507501cfba22d0 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Tue, 27 May 2025 16:48:56 +0200 Subject: [PATCH 03/24] fix: models --- prowler/lib/check/models.py | 2 +- prowler/providers/iac/iac_provider.py | 99 ++++++++++--------- .../providers/iac/lib/arguments/arguments.py | 5 +- 3 files changed, 56 insertions(+), 50 deletions(-) diff --git a/prowler/lib/check/models.py b/prowler/lib/check/models.py index b364da4810..31807947d1 100644 --- a/prowler/lib/check/models.py +++ b/prowler/lib/check/models.py @@ -640,7 +640,7 @@ class CheckReportIAC(Check_Report): self.file_path = finding.get("file_path", "") self.file_line_range = finding.get("file_line_range", []) self.guideline = finding.get("guideline", "") - self.resource = finding.get("resource", "") + self.resource = finding self.severity = finding.get("severity", "UNKNOWN") diff --git a/prowler/providers/iac/iac_provider.py b/prowler/providers/iac/iac_provider.py index 62d5f13c92..f69e4a0a63 100644 --- a/prowler/providers/iac/iac_provider.py +++ b/prowler/providers/iac/iac_provider.py @@ -96,59 +96,62 @@ class IACProvider(Provider): reports = [] + # TODO: Process passed checks # Process failed checks - for finding in output.get("results", {}).get("failed_checks", []): - # Get severity and ensure it's a valid string - severity = finding.get("severity", "low") - if severity is None: - severity = "low" - severity = str(severity).lower() + for finding in output: + for failed_check in finding.get("results", {}).get("failed_checks", []): + # Get severity and ensure it's a valid string + severity = finding.get("severity", "low") + if severity is None: + severity = "low" + severity = str(severity).lower() - # Create a basic metadata structure for the check - metadata_dict = { - "Provider": "iac", - "CheckID": finding.get("check_id", ""), - "CheckTitle": finding.get("check_name", ""), - "CheckType": ["Infrastructure as Code"], - "ServiceName": "iac", - "SubServiceName": "", - "ResourceIdTemplate": "", - "Severity": severity, - "ResourceType": "iac", - "Description": finding.get("check_name", ""), - "Risk": "", - "RelatedUrl": finding.get("guideline", ""), - "Remediation": { - "Code": { - "NativeIaC": "", - "Terraform": "", - "CLI": "", - "Other": "", + # Create a basic metadata structure for the check + metadata_dict = { + "Provider": "iac", + "CheckID": failed_check.get("check_id", ""), + "CheckTitle": failed_check.get("check_name", ""), + "CheckType": ["Infrastructure as Code"], + "ServiceName": "iac", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": severity, + "ResourceType": "iac", + "Description": failed_check.get("check_name", ""), + "Risk": "", + "RelatedUrl": ( + failed_check.get("guideline", "") + if failed_check.get("guideline") + else "" + ), + "Remediation": { + "Code": { + "NativeIaC": "", + "Terraform": "", + "CLI": "", + "Other": "", + }, + "Recommendation": { + "Text": "", + "Url": ( + failed_check.get("guideline", "") + if failed_check.get("guideline") + else "" + ), + }, }, - "Recommendation": { - "Text": "", - "Url": finding.get("guideline", ""), - }, - }, - "Categories": ["security"], - "DependsOn": [], - "RelatedTo": [], - "Notes": "", - } + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + } - # Convert metadata dict to JSON string - metadata = json.dumps(metadata_dict) + # Convert metadata dict to JSON string + metadata = json.dumps(metadata_dict) - report = CheckReportIAC(metadata=metadata, finding=finding) - reports.append(report) - - # Log summary - summary = output.get("summary", {}) - logger.info( - f"IaC scan completed with {summary.get('passed', 0)} passed, " - f"{summary.get('failed', 0)} failed, and " - f"{summary.get('skipped', 0)} skipped checks." - ) + report = CheckReportIAC(metadata=metadata, finding=failed_check) + report.status = "FAIL" + reports.append(report) return reports diff --git a/prowler/providers/iac/lib/arguments/arguments.py b/prowler/providers/iac/lib/arguments/arguments.py index d02d476059..87401aecf1 100644 --- a/prowler/providers/iac/lib/arguments/arguments.py +++ b/prowler/providers/iac/lib/arguments/arguments.py @@ -3,7 +3,10 @@ def init_parser(self): iac_parser = self.subparsers.add_parser( "iac", parents=[self.common_providers_parser], help="IaC Provider" ) - iac_parser.add_argument( + + # Scan Path + iac_scan_subparser = iac_parser.add_argument_group("Scan Path") + iac_scan_subparser.add_argument( "--scan-path", "-P", dest="scan_path", From 01e47d6c8b2b2f258da6ea0b4af66e462d14a9fa Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Tue, 27 May 2025 17:15:10 +0200 Subject: [PATCH 04/24] fix: tests --- .../kubernetes/cis_1.10_kubernetes.json | 230 ++++++++--------- .../kubernetes/cis_1.11_kubernetes.json | 236 +++++++++--------- .../kubernetes/cis_1.8_kubernetes.json | 228 ++++++++--------- prowler/lib/check/compliance_models.py | 35 ++- prowler/lib/check/models.py | 4 +- prowler/lib/outputs/finding.py | 4 +- prowler/providers/common/models.py | 4 - prowler/providers/gcp/models.py | 2 +- prowler/providers/iac/iac_provider.py | 11 +- tests/lib/cli/parser_test.py | 4 +- .../compliance/cis/cis_kubernetes_test.py | 2 +- .../outputs/compliance/cis/cis_m365_test.py | 2 +- tests/lib/outputs/compliance/fixtures.py | 6 +- tests/lib/outputs/finding_test.py | 16 +- 14 files changed, 399 insertions(+), 385 deletions(-) diff --git a/prowler/compliance/kubernetes/cis_1.10_kubernetes.json b/prowler/compliance/kubernetes/cis_1.10_kubernetes.json index 379504a127..f1bdbd9437 100644 --- a/prowler/compliance/kubernetes/cis_1.10_kubernetes.json +++ b/prowler/compliance/kubernetes/cis_1.10_kubernetes.json @@ -12,7 +12,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the API server pod specification file has permissions of `600` or more restrictive.", "RationaleStatement": "The API server pod specification file controls various parameters that set the behavior of the API server. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -33,7 +33,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the API server pod specification file ownership is set to `root:root`.", "RationaleStatement": "The API server pod specification file controls various parameters that set the behavior of the API server. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -54,7 +54,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.", "RationaleStatement": "The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -75,7 +75,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the controller manager pod specification file ownership is set to `root:root`.", "RationaleStatement": "The controller manager pod specification file controls various parameters that set the behavior of various components of the master node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -96,7 +96,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the scheduler pod specification file has permissions of `600` or more restrictive.", "RationaleStatement": "The scheduler pod specification file controls various parameters that set the behavior of the Scheduler service in the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -117,7 +117,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the scheduler pod specification file ownership is set to `root:root`.", "RationaleStatement": "The scheduler pod specification file controls various parameters that set the behavior of the `kube-scheduler` service in the master node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -138,7 +138,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `/etc/kubernetes/manifests/etcd.yaml` file has permissions of `600` or more restrictive.", "RationaleStatement": "The etcd pod specification file `/etc/kubernetes/manifests/etcd.yaml` controls various parameters that set the behavior of the `etcd` service in the master node. etcd is a highly-available key-value store which Kubernetes uses for persistent storage of all of its REST API object. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -159,7 +159,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `/etc/kubernetes/manifests/etcd.yaml` file ownership is set to `root:root`.", "RationaleStatement": "The etcd pod specification file `/etc/kubernetes/manifests/etcd.yaml` controls various parameters that set the behavior of the `etcd` service in the master node. etcd is a highly-available key-value store which Kubernetes uses for persistent storage of all of its REST API object. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -180,7 +180,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Container Network Interface files have permissions of `600` or more restrictive.", "RationaleStatement": "Container Network Interface provides various networking options for overlay networking. You should consult their documentation and restrict their respective file permissions to maintain the integrity of those files. Those files should be writable by only the administrators on the system.", @@ -201,7 +201,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Container Network Interface files have ownership set to `root:root`.", "RationaleStatement": "Container Network Interface provides various networking options for overlay networking. You should consult their documentation and restrict their respective file permissions to maintain the integrity of those files. Those files should be owned by `root:root`.", @@ -222,7 +222,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the etcd data directory has permissions of `700` or more restrictive.", "RationaleStatement": "etcd is a highly-available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. This data directory should be protected from any unauthorized reads or writes. It should not be readable or writable by any group members or the world.", @@ -243,7 +243,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the etcd data directory ownership is set to `etcd:etcd`.", "RationaleStatement": "etcd is a highly-available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. This data directory should be protected from any unauthorized reads or writes. It should be owned by `etcd:etcd`.", @@ -264,7 +264,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `admin.conf` file (and `super-admin.conf` file, where it exists) have permissions of `600`.", "RationaleStatement": "As part of initial cluster setup, default kubeconfig files are created to be used by the administrator of the cluster. These files contain private keys and certificates which allow for privileged access to the cluster. You should restrict their file permissions to maintain the integrity and confidentiality of the file(s). The file(s) should be readable and writable by only the administrators on the system.", @@ -285,7 +285,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `admin.conf` (and `super-admin.conf` file, where it exists) file ownership is set to `root:root`.", "RationaleStatement": "As part of initial cluster setup, default kubeconfig files are created to be used by the administrator of the cluster. These files contain private keys and certificates which allow for privileged access to the cluster. You should set their file ownership to maintain the integrity and confidentiality of the file. The file(s) should be owned by root:root.", @@ -306,7 +306,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `scheduler.conf` file has permissions of `600` or more restrictive.", "RationaleStatement": "The `scheduler.conf` file is the kubeconfig file for the Scheduler. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -327,7 +327,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `scheduler.conf` file ownership is set to `root:root`.", "RationaleStatement": "The `scheduler.conf` file is the kubeconfig file for the Scheduler. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -348,7 +348,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `controller-manager.conf` file has permissions of 600 or more restrictive.", "RationaleStatement": "The `controller-manager.conf` file is the kubeconfig file for the Controller Manager. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -369,7 +369,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `controller-manager.conf` file ownership is set to `root:root`.", "RationaleStatement": "The `controller-manager.conf` file is the kubeconfig file for the Controller Manager. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -390,7 +390,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the Kubernetes PKI directory and file ownership is set to `root:root`.", "RationaleStatement": "Kubernetes makes use of a number of certificates as part of its operation. You should set the ownership of the directory containing the PKI information and all files in that directory to maintain their integrity. The directory and files should be owned by `root:root`.", @@ -411,7 +411,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that Kubernetes PKI certificate files have permissions of `600` or more restrictive.", "RationaleStatement": "Kubernetes makes use of a number of certificate files as part of the operation of its components. The permissions on these files should be set to `600` or more restrictive to protect their integrity and confidentiality.", @@ -432,7 +432,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that Kubernetes PKI key files have permissions of `600`.", "RationaleStatement": "Kubernetes makes use of a number of key files as part of the operation of its components. The permissions on these files should be set to `600` to protect their integrity and confidentiality.", @@ -455,7 +455,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Disable anonymous requests to the API server.", "RationaleStatement": "When enabled, requests that are not rejected by other configured authentication methods are treated as anonymous requests. These requests are then served by the API server. You should rely on authentication to authorize access and disallow anonymous requests.If you are using RBAC authorization, it is generally considered reasonable to allow anonymous access to the API Server for health checks and discovery purposes, and hence this recommendation is not scored. However, you should consider whether anonymous discovery is an acceptable risk for your purposes.", @@ -478,7 +478,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not use token based authentication.", "RationaleStatement": "The token-based authentication utilizes static tokens to authenticate requests to the apiserver. The tokens are stored in clear-text in a file on the apiserver, and cannot be revoked or rotated without restarting the apiserver. Hence, do not use static token-based authentication.", @@ -501,7 +501,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "This admission controller rejects all net-new usage of the Service field externalIPs.", "RationaleStatement": "Most users do not need the ability to set the `externalIPs` field for a `Service` at all, and cluster admins should consider disabling this functionality by enabling the `DenyServiceExternalIPs` admission controller. Clusters that do need to allow this functionality should consider using some custom policy to manage its usage.", @@ -524,7 +524,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable certificate based kubelet authentication.", "RationaleStatement": "The apiserver, by default, does not authenticate itself to the kubelet's HTTPS endpoints. The requests from the apiserver are treated anonymously. You should set up certificate-based kubelet authentication to ensure that the apiserver authenticates itself to kubelets when submitting requests.", @@ -547,7 +547,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Verify kubelet's certificate before establishing connection.", "RationaleStatement": "The connections from the apiserver to the kubelet are used for fetching logs for pods, attaching (through kubectl) to running pods, and using the kubelet’s port-forwarding functionality. These connections terminate at the kubelet’s HTTPS endpoint. By default, the apiserver does not verify the kubelet’s serving certificate, which makes the connection subject to man-in-the-middle attacks, and unsafe to run over untrusted and/or public networks.", @@ -570,7 +570,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not always authorize all requests.", "RationaleStatement": "The API Server, can be configured to allow all requests. This mode should not be used on any production cluster.", @@ -593,7 +593,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Restrict kubelet nodes to reading only objects associated with them.", "RationaleStatement": "The `Node` authorization mode only allows kubelets to read `Secret`, `ConfigMap`, `PersistentVolume`, and `PersistentVolumeClaim` objects associated with their nodes.", @@ -616,7 +616,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Turn on Role Based Access Control.", "RationaleStatement": "Role Based Access Control (RBAC) allows fine-grained control over the operations that different entities can perform on different objects in the cluster. It is recommended to use the RBAC authorization mode.", @@ -639,7 +639,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Limit the rate at which the API server accepts requests.", "RationaleStatement": "Using `EventRateLimit` admission control enforces a limit on the number of events that the API Server will accept in a given time slice. A misbehaving workload could overwhelm and DoS the API Server, making it unavailable. This particularly applies to a multi-tenant cluster, where there might be a small percentage of misbehaving tenants which could have a significant impact on the performance of the cluster overall. Hence, it is recommended to limit the rate of events that the API server will accept.", @@ -662,7 +662,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not allow all requests.", "RationaleStatement": "Setting admission control plugin `AlwaysAdmit` allows all requests and do not filter any requests.The `AlwaysAdmit` admission controller was deprecated in Kubernetes v1.13. Its behavior was equivalent to turning off all admission controllers.", @@ -685,7 +685,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Always pull images.", "RationaleStatement": "Setting admission control policy to `AlwaysPullImages` forces every new pod to pull the required images every time. In a multi-tenant cluster users can be assured that their private images can only be used by those who have the credentials to pull them. Without this admission control policy, once an image has been pulled to a node, any pod from any user can use it simply by knowing the image’s name, without any authorization check against the image ownership. When this plug-in is enabled, images are always pulled prior to starting containers, which means valid credentials are required.", @@ -777,7 +777,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable profiling, if not needed.", "RationaleStatement": "Profiling allows for the identification of specific performance bottlenecks. It generates a significant amount of program data that could potentially be exploited to uncover system and program details. If you are not experiencing any bottlenecks and do not need the profiler for troubleshooting purposes, it is recommended to turn it off to reduce the potential attack surface.", @@ -800,7 +800,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable auditing on the Kubernetes API Server and set the desired audit log path.", "RationaleStatement": "Auditing the Kubernetes API Server provides a security-relevant chronological set of records documenting the sequence of activities that have affected system by individual users, administrators or other components of the system. Even though currently, Kubernetes provides only basic audit capabilities, it should be enabled. You can enable it by setting an appropriate audit log path.", @@ -823,7 +823,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Retain the logs for at least 30 days or as appropriate.", "RationaleStatement": "Retaining logs for at least 30 days ensures that you can go back in time and investigate or correlate any events. Set your audit log retention period to 30 days or as per your business requirements.", @@ -846,7 +846,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Retain 10 or an appropriate number of old log files.", "RationaleStatement": "Kubernetes automatically rotates the log files. Retaining old log files ensures that you would have sufficient log data available for carrying out any investigation or correlation. For example, if you have set file size of 100 MB and the number of old log files to keep as 10, you would approximate have 1 GB of log data that you could potentially use for your analysis.", @@ -869,7 +869,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Rotate log files on reaching 100 MB or as appropriate.", "RationaleStatement": "Kubernetes automatically rotates the log files. Retaining old log files ensures that you would have sufficient log data available for carrying out any investigation or correlation. If you have set file size of 100 MB and the number of old log files to keep as 10, you would approximate have 1 GB of log data that you could potentially use for your analysis.", @@ -892,7 +892,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Set global request timeout for API server requests as appropriate.", "RationaleStatement": "Setting global request timeout allows extending the API server request timeout limit to a duration appropriate to the user's connection speed. By default, it is set to 60 seconds which might be problematic on slower connections making cluster resources inaccessible once the data volume for requests exceeds what can be transmitted in 60 seconds. But, setting this timeout limit to be too large can exhaust the API server resources making it prone to Denial-of-Service attack. Hence, it is recommended to set this limit as appropriate and change the default limit of 60 seconds only if needed.", @@ -915,7 +915,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Validate service account before validating token.", "RationaleStatement": "If `--service-account-lookup` is not enabled, the apiserver only verifies that the authentication token is valid, and does not validate that the service account token mentioned in the request is actually present in etcd. This allows using a service account token even after the corresponding service account is deleted. This is an example of time of check to time of use security issue.", @@ -938,7 +938,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Explicitly set a service account public key file for service accounts on the apiserver.", "RationaleStatement": "By default, if no `--service-account-key-file` is specified to the apiserver, it uses the private key from the TLS serving certificate to verify service account tokens. To ensure that the keys for service account tokens could be rotated as needed, a separate public/private key pair should be used for signing service account tokens. Hence, the public key should be specified to the apiserver with `--service-account-key-file`.", @@ -961,7 +961,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured to make use of TLS encryption for client connections.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be protected by client authentication. This requires the API server to identify itself to the etcd server using a client certificate and key.", @@ -984,7 +984,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Setup TLS connection on the API server.", "RationaleStatement": "API server communication contains sensitive parameters that should remain encrypted in transit. Configure the API server to serve only HTTPS traffic.", @@ -1007,7 +1007,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Setup TLS connection on the API server.", "RationaleStatement": "API server communication contains sensitive parameters that should remain encrypted in transit. Configure the API server to serve only HTTPS traffic. If `--client-ca-file` argument is set, any request presenting a client certificate signed by one of the authorities in the `client-ca-file` is authenticated with an identity corresponding to the CommonName of the client certificate.", @@ -1030,7 +1030,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured to make use of TLS encryption for client connections.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be protected by client authentication. This requires the API server to identify itself to the etcd server using a SSL Certificate Authority file.", @@ -1053,7 +1053,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Encrypt etcd key-value store.", "RationaleStatement": "etcd is a highly available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be encrypted at rest to avoid any disclosures.", @@ -1074,7 +1074,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Where `etcd` encryption is used, appropriate providers should be configured.", "RationaleStatement": "Where `etcd` encryption is used, it is important to ensure that the appropriate set of encryption providers is used. Currently, the `aescbc`, `kms` and `secretbox` are likely to be appropriate options.", @@ -1097,7 +1097,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the API server is configured to only use strong cryptographic ciphers.", "RationaleStatement": "TLS ciphers have had a number of known vulnerabilities and weaknesses, which can reduce the protection provided by them. By default Kubernetes supports a number of TLS ciphersuites including some that have security concerns, weakening the protection provided.", @@ -1120,7 +1120,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Activate garbage collector on pod termination, as appropriate.", "RationaleStatement": "Garbage collection is important to ensure sufficient resource availability and avoiding degraded performance and availability. In the worst case, the system might crash or just be unusable for a long period of time. The current setting for garbage collection is 12,500 terminated pods which might be too high for your system to sustain. Based on your system resources and tests, choose an appropriate threshold value to activate garbage collection.", @@ -1143,7 +1143,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable profiling, if not needed.", "RationaleStatement": "Profiling allows for the identification of specific performance bottlenecks. It generates a significant amount of program data that could potentially be exploited to uncover system and program details. If you are not experiencing any bottlenecks and do not need the profiler for troubleshooting purposes, it is recommended to turn it off to reduce the potential attack surface.", @@ -1166,7 +1166,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Use individual service account credentials for each controller.", "RationaleStatement": "The controller manager creates a service account per controller in the `kube-system` namespace, generates a credential for it, and builds a dedicated API client with that service account credential for each controller loop to use. Setting the `--use-service-account-credentials` to `true` runs each control loop within the controller manager using a separate service account credential. When used in combination with RBAC, this ensures that the control loops run with the minimum permissions required to perform their intended tasks.", @@ -1189,7 +1189,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Explicitly set a service account private key file for service accounts on the controller manager.", "RationaleStatement": "To ensure that keys for service account tokens can be rotated as needed, a separate public/private key pair should be used for signing service account tokens. The private key should be specified to the controller manager with `--service-account-private-key-file` as appropriate.", @@ -1212,7 +1212,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Allow pods to verify the API server's serving certificate before establishing connections.", "RationaleStatement": "Processes running within pods that need to contact the API server must verify the API server's serving certificate. Failing to do so could be a subject to man-in-the-middle attacks.Providing the root certificate for the API server's serving certificate to the controller manager with the `--root-ca-file` argument allows the controller manager to inject the trusted bundle into pods so that they can verify TLS connections to the API server.", @@ -1235,7 +1235,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable kubelet server certificate rotation on controller-manager.", "RationaleStatement": "`RotateKubeletServerCertificate` causes the kubelet to both request a serving certificate after bootstrapping its client credentials and rotate the certificate as its existing credentials expire. This automated periodic rotation ensures that the there are no downtimes due to expired certificates and thus addressing availability in the CIA security triad.Note: This recommendation only applies if you let kubelets get their certificates from the API server. In case your kubelet certificates come from an outside authority/tool (e.g. Vault) then you need to take care of rotation yourself.", @@ -1258,7 +1258,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not bind the Controller Manager service to non-loopback insecure addresses.", "RationaleStatement": "The Controller Manager API service which runs on port 10252/TCP by default is used for health and metrics information and is available without authentication or encryption. As such it should only be bound to a localhost interface, to minimize the cluster's attack surface", @@ -1281,7 +1281,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.4 Scheduler", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable profiling, if not needed.", "RationaleStatement": "Profiling allows for the identification of specific performance bottlenecks. It generates a significant amount of program data that could potentially be exploited to uncover system and program details. If you are not experiencing any bottlenecks and do not need the profiler for troubleshooting purposes, it is recommended to turn it off to reduce the potential attack surface.", @@ -1304,7 +1304,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.4 Scheduler", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not bind the scheduler service to non-loopback insecure addresses.", "RationaleStatement": "The Scheduler API service which runs on port 10251/TCP by default is used for health and metrics information and is available without authentication or encryption. As such it should only be bound to a localhost interface, to minimize the cluster's attack surface", @@ -1326,7 +1326,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Configure TLS encryption for the etcd service.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be encrypted in transit.", @@ -1348,7 +1348,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable client authentication on etcd service.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should not be available to unauthenticated clients. You should enable the client authentication via valid certificates to secure the access to the etcd service.", @@ -1370,7 +1370,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not use self-signed certificates for TLS.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should not be available to unauthenticated clients. You should enable the client authentication via valid certificates to secure the access to the etcd service.", @@ -1392,7 +1392,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured to make use of TLS encryption for peer connections.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be encrypted in transit and also amongst peers in the etcd clusters.", @@ -1414,7 +1414,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured for peer authentication.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be accessible only by authenticated etcd peers in the etcd cluster.", @@ -1436,7 +1436,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not use automatically generated self-signed certificates for TLS connections between peers.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be accessible only by authenticated etcd peers in the etcd cluster. Hence, do not use self-signed certificates for authentication.", @@ -1479,7 +1479,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.1 Authentication and Authorization", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes provides the option to use client certificates for user authentication. However as there is no way to revoke these certificates when a user leaves an organization or loses their credential, they are not suitable for this purpose.It is not possible to fully disable client certificate use within a cluster as it is used for component to component authentication.", "RationaleStatement": "With any authentication mechanism the ability to revoke credentials if they are compromised or no longer required, is a key control. Kubernetes client certificate authentication does not allow for this due to a lack of support for certificate revocation.", @@ -1500,7 +1500,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.1 Authentication and Authorization", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes provides service account tokens which are intended for use by workloads running in the Kubernetes cluster, for authentication to the API server.These tokens are not designed for use by end-users and do not provide for features such as revocation or expiry, making them insecure. A newer version of the feature (Bound service account token volumes) does introduce expiry but still does not allow for specific revocation.", "RationaleStatement": "With any authentication mechanism the ability to revoke credentials if they are compromised or no longer required, is a key control. Service account token authentication does not allow for this due to the use of JWT tokens as an underlying technology.", @@ -1521,7 +1521,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.1 Authentication and Authorization", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes provides bootstrap tokens which are intended for use by new nodes joining the clusterThese tokens are not designed for use by end-users they are specifically designed for the purpose of bootstrapping new nodes and not for general authentication", "RationaleStatement": "Bootstrap tokens are not intended for use as a general authentication mechanism and impose constraints on user and group naming that do not facilitate good RBAC design. They also cannot be used with MFA resulting in a weak authentication mechanism being available.", @@ -1542,7 +1542,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.2 Logging", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes can audit the details of requests made to the API server. The `--audit-policy-file` flag must be set for this logging to be enabled.", "RationaleStatement": "Logging is an important detective control for all systems, to detect potential unauthorised access.", @@ -1586,7 +1586,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet` service file has permissions of `600` or more restrictive.", "RationaleStatement": "The `kubelet` service file controls various parameters that set the behavior of the `kubelet` service in the worker node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1609,7 +1609,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet` service file ownership is set to `root:root`.", "RationaleStatement": "The `kubelet` service file controls various parameters that set the behavior of the `kubelet` service in the worker node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1630,7 +1630,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "If `kube-proxy` is running, and if it is using a file-based kubeconfig file, ensure that the proxy kubeconfig file has permissions of `600` or more restrictive.", "RationaleStatement": "The `kube-proxy` kubeconfig file controls various parameters of the `kube-proxy` service in the worker node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.It is possible to run `kube-proxy` with the kubeconfig parameters configured as a Kubernetes ConfigMap instead of a file. In this case, there is no proxy kubeconfig file.", @@ -1651,7 +1651,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "If `kube-proxy` is running, ensure that the file ownership of its kubeconfig file is set to `root:root`.", "RationaleStatement": "The kubeconfig file for `kube-proxy` controls various parameters for the `kube-proxy` service in the worker node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1674,7 +1674,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet.conf` file has permissions of `600` or more restrictive.", "RationaleStatement": "The `kubelet.conf` file is the kubeconfig file for the node, and controls various parameters that set the behavior and identity of the worker node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1697,7 +1697,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet.conf` file ownership is set to `root:root`.", "RationaleStatement": "The `kubelet.conf` file is the kubeconfig file for the node, and controls various parameters that set the behavior and identity of the worker node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1718,7 +1718,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the certificate authorities file has permissions of `600` or more restrictive.", "RationaleStatement": "The certificate authorities file controls the authorities used to validate API requests. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1739,7 +1739,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the certificate authorities file ownership is set to `root:root`.", "RationaleStatement": "The certificate authorities file controls the authorities used to validate API requests. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1762,7 +1762,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that if the kubelet refers to a configuration file with the `--config` argument, that file has permissions of 600 or more restrictive.", "RationaleStatement": "The kubelet reads various parameters, including security settings, from a config file specified by the `--config` argument. If this file is specified you should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1785,7 +1785,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that if the kubelet refers to a configuration file with the `--config` argument, that file is owned by root:root.", "RationaleStatement": "The kubelet reads various parameters, including security settings, from a config file specified by the `--config` argument. If this file is specified you should restrict its file permissions to maintain the integrity of the file. The file should be owned by root:root.", @@ -1808,7 +1808,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable anonymous requests to the Kubelet server.", "RationaleStatement": "When enabled, requests that are not rejected by other configured authentication methods are treated as anonymous requests. These requests are then served by the Kubelet server. You should rely on authentication to authorize access and disallow anonymous requests.", @@ -1831,7 +1831,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not allow all requests. Enable explicit authorization.", "RationaleStatement": "Kubelets, by default, allow all authenticated requests (even anonymous ones) without needing explicit authorization checks from the apiserver. You should restrict this behavior and only allow explicitly authorized requests.", @@ -1854,7 +1854,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable Kubelet authentication using certificates.", "RationaleStatement": "The connections from the apiserver to the kubelet are used for fetching logs for pods, attaching (through kubectl) to running pods, and using the kubelet’s port-forwarding functionality. These connections terminate at the kubelet’s HTTPS endpoint. By default, the apiserver does not verify the kubelet’s serving certificate, which makes the connection subject to man-in-the-middle attacks, and unsafe to run over untrusted and/or public networks. Enabling Kubelet certificate authentication ensures that the apiserver could authenticate the Kubelet before submitting any requests.", @@ -1877,7 +1877,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Disable the read-only port.", "RationaleStatement": "The Kubelet process provides a read-only API in addition to the main Kubelet API. Unauthenticated access is provided to this read-only API which could possibly retrieve potentially sensitive information about the cluster.", @@ -1900,7 +1900,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not disable timeouts on streaming connections.", "RationaleStatement": "Setting idle timeouts ensures that you are protected against Denial-of-Service attacks, inactive connections and running out of ephemeral ports. **Note:** By default, `--streaming-connection-idle-timeout` is set to 4 hours which might be too high for your environment. Setting this as appropriate would additionally ensure that such streaming connections are timed out after serving legitimate use cases.", @@ -1923,7 +1923,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Allow Kubelet to manage iptables.", "RationaleStatement": "Kubelets can automatically manage the required changes to iptables based on how you choose your networking options for the pods. It is recommended to let kubelets manage the changes to iptables. This ensures that the iptables configuration remains in sync with pods networking configuration. Manually configuring iptables with dynamic pod network configuration changes might hamper the communication between pods/containers and to the outside world. You might have iptables rules too restrictive or too open.", @@ -1944,7 +1944,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not override node hostnames.", "RationaleStatement": "Overriding hostnames could potentially break TLS setup between the kubelet and the apiserver. Additionally, with overridden hostnames, it becomes increasingly difficult to associate logs with a particular node and process them for security analytics. Hence, you should setup your kubelet nodes with resolvable FQDNs and avoid overriding the hostnames with IPs.", @@ -1990,7 +1990,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Setup TLS connection on the Kubelets.", "RationaleStatement": "The connections from the apiserver to the kubelet are used for fetching logs for pods, attaching (through kubectl) to running pods, and using the kubelet’s port-forwarding functionality. These connections terminate at the kubelet’s HTTPS endpoint. By default, the apiserver does not verify the kubelet’s serving certificate, which makes the connection subject to man-in-the-middle attacks, and unsafe to run over untrusted and/or public networks.", @@ -2013,7 +2013,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable kubelet client certificate rotation.", "RationaleStatement": "The `--rotate-certificates` setting causes the kubelet to rotate its client certificates by creating new CSRs as its existing credentials expire. This automated periodic rotation ensures that the there is no downtime due to expired certificates and thus addressing availability in the CIA security triad.**Note:** This recommendation only applies if you let kubelets get their certificates from the API server. In case your kubelet certificates come from an outside authority/tool (e.g. Vault) then you need to take care of rotation yourself.**Note:** This feature also require the `RotateKubeletClientCertificate` feature gate to be enabled (which is the default since Kubernetes v1.7)", @@ -2034,7 +2034,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Enable kubelet server certificate rotation.", "RationaleStatement": "`RotateKubeletServerCertificate` causes the kubelet to both request a serving certificate after bootstrapping its client credentials and rotate the certificate as its existing credentials expire. This automated periodic rotation ensures that the there are no downtimes due to expired certificates and thus addressing availability in the CIA security triad.Note: This recommendation only applies if you let kubelets get their certificates from the API server. In case your kubelet certificates come from an outside authority/tool (e.g. Vault) then you need to take care of rotation yourself.", @@ -2057,7 +2057,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Kubelet is configured to only use strong cryptographic ciphers.", "RationaleStatement": "TLS ciphers have had a number of known vulnerabilities and weaknesses, which can reduce the protection provided by them. By default Kubernetes supports a number of TLS ciphersuites including some that have security concerns, weakening the protection provided.", @@ -2078,7 +2078,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Kubelet sets limits on the number of PIDs that can be created by pods running on the node.", "RationaleStatement": "By default pods running in a cluster can consume any number of PIDs, potentially exhausting the resources available on the node. Setting an appropriate limit reduces the risk of a denial of service attack on cluster nodes.", @@ -2099,7 +2099,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.3 kube-proxy", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not bind the kube-proxy metrics port to non-loopback addresses.", "RationaleStatement": "kube-proxy has two APIs which provided access to information about the service and can be bound to network ports. The metrics API service includes endpoints (`/metrics` and `/configz`) which disclose information about the configuration and operation of kube-proxy. These endpoints should not be exposed to untrusted networks as they do not support encryption or authentication to restrict access to the data they provide.", @@ -2122,7 +2122,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "The RBAC role `cluster-admin` provides wide-ranging powers over the environment and should be used only where and when needed.", "RationaleStatement": "Kubernetes provides a set of default roles where RBAC is used. Some of these roles such as `cluster-admin` provide wide-ranging privileges which should only be applied where absolutely necessary. Roles such as `cluster-admin` allow super-user access to perform any action on any resource. When used in a `ClusterRoleBinding`, it gives full control over every resource in the cluster and in all namespaces. When used in a `RoleBinding`, it gives full control over every resource in the rolebinding's namespace, including the namespace itself.", @@ -2145,7 +2145,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "The Kubernetes API stores secrets, which may be service account tokens for the Kubernetes API or credentials used by workloads in the cluster. Access to these secrets should be restricted to the smallest possible group of users to reduce the risk of privilege escalation.", "RationaleStatement": "Inappropriate access to secrets stored within the Kubernetes cluster can allow for an attacker to gain additional access to the Kubernetes cluster or external resources whose credentials are stored as secrets.", @@ -2168,7 +2168,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Kubernetes Roles and ClusterRoles provide access to resources based on sets of objects and actions that can be taken on those objects. It is possible to set either of these to be the wildcard \"*\" which matches all items. Use of wildcards is not optimal from a security perspective as it may allow for inadvertent access to be granted when new resources are added to the Kubernetes API either as CRDs or in later versions of the product.", "RationaleStatement": "The principle of least privilege recommends that users are provided only the access required for their role and nothing more. The use of wildcard rights grants is likely to provide excessive rights to the Kubernetes API.", @@ -2191,7 +2191,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "The ability to create pods in a namespace can provide a number of opportunities for privilege escalation, such as assigning privileged service accounts to these pods or mounting hostPaths with access to sensitive data (unless Pod Security Policies are implemented to restrict this access)As such, access to create new pods should be restricted to the smallest possible group of users.", "RationaleStatement": "The ability to create pods in a cluster opens up possibilities for privilege escalation and should be restricted, where possible.", @@ -2212,7 +2212,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "The `default` service account should not be used to ensure that rights granted to applications can be more easily audited and reviewed.", "RationaleStatement": "Kubernetes provides a `default` service account which is used by cluster workloads where no specific service account is assigned to the pod.Where access to the Kubernetes API from a pod is required, a specific service account should be created for that pod, and rights granted to that service account.The default service account should be configured such that it does not provide a service account token and does not have any explicit rights assignments.", @@ -2233,7 +2233,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Service accounts tokens should not be mounted in pods except where the workload running in the pod explicitly needs to communicate with the API server", "RationaleStatement": "Mounting service account tokens inside pods can provide an avenue for privilege escalation attacks where an attacker is able to compromise a single pod in the cluster.Avoiding mounting these tokens removes this attack avenue.", @@ -2254,7 +2254,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The special group `system:masters` should not be used to grant permissions to any user or service account, except where strictly necessary (e.g. bootstrapping access prior to RBAC being fully available)", "RationaleStatement": "The `system:masters` group has unrestricted access to the Kubernetes API hard-coded into the API server source code. An authenticated user who is a member of this group cannot have their access reduced, even if all bindings and cluster role bindings which mention it, are removed.When combined with client certificate authentication, use of this group can allow for irrevocable cluster-admin level credentials to exist for a cluster.", @@ -2275,7 +2275,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Cluster roles and roles with the impersonate, bind or escalate permissions should not be granted unless strictly required. Each of these permissions allow a particular subject to escalate their privileges beyond those explicitly granted by cluster administrators", "RationaleStatement": "The impersonate privilege allows a subject to impersonate other users gaining their rights to the cluster. The bind privilege allows the subject to add a binding to a cluster role or role which escalates their effective permissions in the cluster. The escalate privilege allows a subject to modify cluster roles to which they are bound, increasing their rights to that level.Each of these permissions has the potential to allow for privilege escalation to cluster-admin level.", @@ -2298,7 +2298,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The ability to create persistent volumes in a cluster can provide an opportunity for privilege escalation, via the creation of `hostPath` volumes. As persistent volumes are not covered by Pod Security Admission, a user with access to create persistent volumes may be able to get access to sensitive files from the underlying host even where restrictive Pod Security Admission policies are in place.", "RationaleStatement": "The ability to create persistent volumes in a cluster opens up possibilities for privilege escalation and should be restricted, where possible.", @@ -2321,7 +2321,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with access to the `Proxy` sub-resource of `Node` objects automatically have permissions to use the Kubelet API, which may allow for privilege escalation or bypass cluster security controls such as audit logs.The Kubelet provides an API which includes rights to execute commands in any container running on the node. Access to this API is covered by permissions to the main Kubernetes API via the `node` object. The proxy sub-resource specifically allows wide ranging access to the Kubelet API.Direct access to the Kubelet API bypasses controls like audit logging (there is no audit log of Kubelet API access) and admission control.", "RationaleStatement": "The ability to use the `proxy` sub-resource of `node` objects opens up possibilities for privilege escalation and should be restricted, where possible.", @@ -2344,7 +2344,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with access to the update the `approval` sub-resource of `certificateaigningrequests` objects can approve new client certificates for the Kubernetes API effectively allowing them to create new high-privileged user accounts.This can allow for privilege escalation to full cluster administrator, depending on users configured in the cluster", "RationaleStatement": "The ability to update certificate signing requests should be limited.", @@ -2367,7 +2367,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with rights to create/modify/delete `validatingwebhookconfigurations` or `mutatingwebhookconfigurations` can control webhooks that can read any object admitted to the cluster, and in the case of mutating webhooks, also mutate admitted objects. This could allow for privilege escalation or disruption of the operation of the cluster.", "RationaleStatement": "The ability to manage webhook configuration should be limited", @@ -2390,7 +2390,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with rights to create new service account tokens at a cluster level, can create long-lived privileged credentials in the cluster. This could allow for privilege escalation and persistent access to the cluster, even if the users account has been revoked.", "RationaleStatement": "The ability to create service account tokens should be limited.", @@ -2411,7 +2411,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Every Kubernetes cluster should have at least one policy control mechanism in place to enforce the other requirements in this section. This could be the in-built Pod Security Admission controller, or a third party policy control system.", "RationaleStatement": "Without an active policy control mechanism, it is not possible to limit the use of containers with access to underlying cluster nodes, via mechanisms like privileged containers, or the use of hostPath volume mounts.", @@ -2434,7 +2434,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `securityContext.privileged` flag set to `true`.", "RationaleStatement": "Privileged containers have access to all Linux Kernel capabilities and devices. A container running with full privileges can do almost everything that the host can do. This flag exists to allow special use-cases, like manipulating the network stack and accessing devices. There should be at least one admission control policy defined which does not permit privileged containers. If you need to run privileged containers, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2457,7 +2457,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `hostPID` flag set to true.", "RationaleStatement": "A container running in the host's PID namespace can inspect processes running outside the container. If the container also has access to ptrace capabilities this can be used to escalate privileges outside of the container.There should be at least one admission control policy defined which does not permit containers to share the host PID namespace.If you need to run containers which require hostPID, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2480,7 +2480,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `hostIPC` flag set to true.", "RationaleStatement": "A container running in the host's IPC namespace can use IPC to interact with processes outside the container.There should be at least one admission control policy defined which does not permit containers to share the host IPC namespace.If you need to run containers which require hostIPC, this should be definited in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2503,7 +2503,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `hostNetwork` flag set to true.", "RationaleStatement": "A container running in the host's network namespace could access the local loopback device, and could access network traffic to and from other pods.There should be at least one admission control policy defined which does not permit containers to share the host network namespace.If you need to run containers which require access to the host's network namesapces, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2526,7 +2526,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `allowPrivilegeEscalation` flag set to true. Allowing this right can lead to a process running a container getting more rights than it started with.It's important to note that these rights are still constrained by the overall container sandbox, and this setting does not relate to the use of privileged containers.", "RationaleStatement": "A container running with the `allowPrivilegeEscalation` flag set to `true` may have processes that can gain more privileges than their parent.There should be at least one admission control policy defined which does not permit containers to allow privilege escalation. The option exists (and is defaulted to true) to permit setuid binaries to run. If you have need to run containers which use setuid binaries or require privilege escalation, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2572,7 +2572,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers with the potentially dangerous NET_RAW capability.", "RationaleStatement": "Containers run with a default set of capabilities as assigned by the Container Runtime. By default this can include potentially dangerous capabilities. With Docker as the container runtime the NET_RAW capability is enabled which may be misused by malicious containers.Ideally, all containers should drop this capability.There should be at least one admission control policy defined which does not permit containers with the NET_RAW capability.If you need to run containers with this capability, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2595,7 +2595,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers with capabilities assigned beyond the default set.", "RationaleStatement": "Containers run with a default set of capabilities as assigned by the Container Runtime. Capabilities outside this set can be added to containers which could expose them to risks of container breakout attacks.There should be at least one policy defined which prevents containers with capabilities beyond the default set from launching.If you need to run containers with additional capabilities, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2641,7 +2641,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit Windows containers to be run with the `hostProcess` flag set to true.", "RationaleStatement": "A Windows container making use of the `hostProcess` flag can interact with the underlying Windows cluster node. As per the Kubernetes documentation, this provides \"privileged access\" to the Windows node.Where Windows containers are used inside a Kubernetes cluster, there should be at least one admission control policy which does not permit `hostProcess` Windows containers.If you need to run Windows containers which require `hostProcess`, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2662,7 +2662,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally admit containers which make use of `hostPath` volumes.", "RationaleStatement": "A container which mounts a `hostPath` volume as part of its specification will have access to the filesystem of the underlying cluster node. The use of `hostPath` volumes may allow containers access to privileged areas of the node filesystem.There should be at least one admission control policy defined which does not permit containers to mount `hostPath` volumes.If you need to run containers which require `hostPath` volumes, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2685,7 +2685,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers which require the use of HostPorts.", "RationaleStatement": "Host ports connect containers directly to the host's network. This can bypass controls such as network policy.There should be at least one admission control policy defined which does not permit containers which require the use of HostPorts.If you need to run containers which require HostPorts, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2706,7 +2706,7 @@ { "Section": "5 Policies", "SubSection": "5.3 Network Policies and CNI", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "There are a variety of CNI plugins available for Kubernetes. If the CNI in use does not support Network Policies it may not be possible to effectively restrict traffic in the cluster.", "RationaleStatement": "Kubernetes network policies are enforced by the CNI plugin in use. As such it is important to ensure that the CNI plugin supports both Ingress and Egress network policies.", @@ -2813,7 +2813,7 @@ { "Section": "5 Policies", "SubSection": "5.7 General Policies", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Use namespaces to isolate your Kubernetes objects.", "RationaleStatement": "Limiting the scope of user permissions can reduce the impact of mistakes or malicious activities. A Kubernetes namespace allows you to partition created resources into logically named groups. Resources created in one namespace can be hidden from other namespaces. By default, each resource created by a user in Kubernetes cluster runs in a default namespace, called `default`. You can create additional namespaces and attach resources and users to them. You can use Kubernetes Authorization plugins to create policies that segregate access to namespace resources between different users.", diff --git a/prowler/compliance/kubernetes/cis_1.11_kubernetes.json b/prowler/compliance/kubernetes/cis_1.11_kubernetes.json index d091e42bae..0faaa94196 100644 --- a/prowler/compliance/kubernetes/cis_1.11_kubernetes.json +++ b/prowler/compliance/kubernetes/cis_1.11_kubernetes.json @@ -12,7 +12,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the API server pod specification file has permissions of `600` or more restrictive.", "RationaleStatement": "The API server pod specification file controls various parameters that set the behavior of the API server. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -33,7 +33,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the API server pod specification file ownership is set to `root:root`.", "RationaleStatement": "The API server pod specification file controls various parameters that set the behavior of the API server. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -54,7 +54,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.", "RationaleStatement": "The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -75,7 +75,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the controller manager pod specification file ownership is set to `root:root`.", "RationaleStatement": "The controller manager pod specification file controls various parameters that set the behavior of various components of the master node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -96,7 +96,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the scheduler pod specification file has permissions of `600` or more restrictive.", "RationaleStatement": "The scheduler pod specification file controls various parameters that set the behavior of the Scheduler service in the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -117,7 +117,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the scheduler pod specification file ownership is set to `root:root`.", "RationaleStatement": "The scheduler pod specification file controls various parameters that set the behavior of the `kube-scheduler` service in the master node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -138,7 +138,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `/etc/kubernetes/manifests/etcd.yaml` file has permissions of `600` or more restrictive.", "RationaleStatement": "The etcd pod specification file `/etc/kubernetes/manifests/etcd.yaml` controls various parameters that set the behavior of the `etcd` service in the master node. etcd is a highly-available key-value store which Kubernetes uses for persistent storage of all of its REST API object. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -159,7 +159,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `/etc/kubernetes/manifests/etcd.yaml` file ownership is set to `root:root`.", "RationaleStatement": "The etcd pod specification file `/etc/kubernetes/manifests/etcd.yaml` controls various parameters that set the behavior of the `etcd` service in the master node. etcd is a highly-available key-value store which Kubernetes uses for persistent storage of all of its REST API object. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -180,7 +180,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Container Network Interface files have permissions of `600` or more restrictive.", "RationaleStatement": "Container Network Interface provides various networking options for overlay networking. You should consult their documentation and restrict their respective file permissions to maintain the integrity of those files. Those files should be writable by only the administrators on the system.", @@ -201,7 +201,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Container Network Interface files have ownership set to `root:root`.", "RationaleStatement": "Container Network Interface provides various networking options for overlay networking. You should consult their documentation and restrict their respective file permissions to maintain the integrity of those files. Those files should be owned by `root:root`.", @@ -222,7 +222,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the etcd data directory has permissions of `700` or more restrictive.", "RationaleStatement": "etcd is a highly-available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. This data directory should be protected from any unauthorized reads or writes. It should not be readable or writable by any group members or the world.", @@ -243,7 +243,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the etcd data directory ownership is set to `etcd:etcd`.", "RationaleStatement": "etcd is a highly-available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. This data directory should be protected from any unauthorized reads or writes. It should be owned by `etcd:etcd`.", @@ -264,7 +264,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `admin.conf` file (and `super-admin.conf` file, where it exists) have permissions of `600`.", "RationaleStatement": "As part of initial cluster setup, default kubeconfig files are created to be used by the administrator of the cluster. These files contain private keys and certificates which allow for privileged access to the cluster. You should restrict their file permissions to maintain the integrity and confidentiality of the file(s). The file(s) should be readable and writable by only the administrators on the system.", @@ -285,7 +285,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `admin.conf` (and `super-admin.conf` file, where it exists) file ownership is set to `root:root`.", "RationaleStatement": "As part of initial cluster setup, default kubeconfig files are created to be used by the administrator of the cluster. These files contain private keys and certificates which allow for privileged access to the cluster. You should set their file ownership to maintain the integrity and confidentiality of the file. The file(s) should be owned by `root:root`.", @@ -306,7 +306,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `scheduler.conf` file has permissions of `600` or more restrictive.", "RationaleStatement": "The `scheduler.conf` file is the kubeconfig file for the Scheduler. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -327,7 +327,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `scheduler.conf` file ownership is set to `root:root`.", "RationaleStatement": "The `scheduler.conf` file is the kubeconfig file for the Scheduler. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -348,7 +348,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `controller-manager.conf` file has permissions of 600 or more restrictive.", "RationaleStatement": "The `controller-manager.conf` file is the kubeconfig file for the Controller Manager. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -369,7 +369,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `controller-manager.conf` file ownership is set to `root:root`.", "RationaleStatement": "The `controller-manager.conf` file is the kubeconfig file for the Controller Manager. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -390,7 +390,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the Kubernetes PKI directory and file ownership is set to `root:root`.", "RationaleStatement": "Kubernetes makes use of a number of certificates as part of its operation. You should set the ownership of the directory containing the PKI information and all files in that directory to maintain their integrity. The directory and files should be owned by `root:root`.", @@ -411,7 +411,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that Kubernetes PKI certificate files have permissions of `644` or more restrictive.", "RationaleStatement": "Kubernetes makes use of a number of certificate files as part of the operation of its components. The permissions on these files should be set to `644` or more restrictive to protect their integrity and confidentiality.", @@ -432,7 +432,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that Kubernetes PKI key files have permissions of `600`.", "RationaleStatement": "Kubernetes makes use of a number of key files as part of the operation of its components. The permissions on these files should be set to `600` to protect their integrity and confidentiality.", @@ -455,7 +455,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Disable anonymous requests to the API server.", "RationaleStatement": "When enabled, requests that are not rejected by other configured authentication methods are treated as anonymous requests. These requests are then served by the API server. You should rely on authentication to authorize access and disallow anonymous requests. If you are using RBAC authorization, it is generally considered reasonable to allow anonymous access to the API Server for health checks and discovery purposes, and hence this recommendation is not scored. However, you should consider whether anonymous discovery is an acceptable risk for your purposes.", @@ -478,7 +478,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not use token based authentication.", "RationaleStatement": "The token-based authentication utilizes static tokens to authenticate requests to the apiserver. The tokens are stored in clear-text in a file on the apiserver, and cannot be revoked or rotated without restarting the apiserver. Hence, do not use static token-based authentication.", @@ -501,7 +501,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "This admission controller rejects all net-new usage of the Service field externalIPs.", "RationaleStatement": "Most users do not need the ability to set the `externalIPs` field for a `Service` at all, and cluster admins should consider disabling this functionality by enabling the `DenyServiceExternalIPs` admission controller. Clusters that do need to allow this functionality should consider using some custom policy to manage its usage.", @@ -524,7 +524,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable certificate based kubelet authentication.", "RationaleStatement": "The apiserver, by default, does not authenticate itself to the kubelet's HTTPS endpoints. The requests from the apiserver are treated anonymously. You should set up certificate-based kubelet authentication to ensure that the apiserver authenticates itself to kubelets when submitting requests.", @@ -547,7 +547,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Verify kubelet's certificate before establishing connection.", "RationaleStatement": "The connections from the apiserver to the kubelet are used for fetching logs for pods, attaching (through kubectl) to running pods, and using the kubelet’s port-forwarding functionality. These connections terminate at the kubelet’s HTTPS endpoint. By default, the apiserver does not verify the kubelet’s serving certificate, which makes the connection subject to man-in-the-middle attacks, and unsafe to run over untrusted and/or public networks.", @@ -570,7 +570,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not always authorize all requests.", "RationaleStatement": "The API Server, can be configured to allow all requests. This mode should not be used on any production cluster.", @@ -593,7 +593,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Restrict kubelet nodes to reading only objects associated with them.", "RationaleStatement": "The `Node` authorization mode only allows kubelets to read `Secret`, `ConfigMap`, `PersistentVolume`, and `PersistentVolumeClaim` objects associated with their nodes.", @@ -616,7 +616,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Turn on Role Based Access Control.", "RationaleStatement": "Role Based Access Control (RBAC) allows fine-grained control over the operations that different entities can perform on different objects in the cluster. It is recommended to use the RBAC authorization mode.", @@ -639,7 +639,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Limit the rate at which the API server accepts requests.", "RationaleStatement": "Using `EventRateLimit` admission control enforces a limit on the number of events that the API Server will accept in a given time slice. A misbehaving workload could overwhelm and DoS the API Server, making it unavailable. This particularly applies to a multi-tenant cluster, where there might be a small percentage of misbehaving tenants which could have a significant impact on the performance of the cluster overall. Hence, it is recommended to limit the rate of events that the API server will accept.", @@ -662,7 +662,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not allow all requests.", "RationaleStatement": "Setting admission control plugin `AlwaysAdmit` allows all requests and do not filter any requests. The `AlwaysAdmit` admission controller was deprecated in Kubernetes v1.13. Its behavior was equivalent to turning off all admission controllers.", @@ -685,7 +685,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Always pull images.", "RationaleStatement": "Setting admission control policy to `AlwaysPullImages` forces every new pod to pull the required images every time. In a multi-tenant cluster users can be assured that their private images can only be used by those who have the credentials to pull them. Without this admission control policy, once an image has been pulled to a node, any pod from any user can use it simply by knowing the image’s name, without any authorization check against the image ownership. When this plug-in is enabled, images are always pulled prior to starting containers, which means valid credentials are required.", @@ -777,7 +777,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable profiling, if not needed.", "RationaleStatement": "Profiling allows for the identification of specific performance bottlenecks. It generates a significant amount of program data that could potentially be exploited to uncover system and program details. If you are not experiencing any bottlenecks and do not need the profiler for troubleshooting purposes, it is recommended to turn it off to reduce the potential attack surface.", @@ -800,7 +800,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable auditing on the Kubernetes API Server and set the desired audit log path.", "RationaleStatement": "Auditing the Kubernetes API Server provides a security-relevant chronological set of records documenting the sequence of activities that have affected system by individual users, administrators or other components of the system. Even though currently, Kubernetes provides only basic audit capabilities, it should be enabled. You can enable it by setting an appropriate audit log path.", @@ -823,7 +823,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Retain the logs for at least 30 days or as appropriate.", "RationaleStatement": "Retaining logs for at least 30 days ensures that you can go back in time and investigate or correlate any events. Set your audit log retention period to 30 days or as per your business requirements.", @@ -846,7 +846,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Retain 10 or an appropriate number of old log files.", "RationaleStatement": "Kubernetes automatically rotates the log files. Retaining old log files ensures that you would have sufficient log data available for carrying out any investigation or correlation. For example, if you have set file size of 100 MB and the number of old log files to keep as 10, you would approximate have 1 GB of log data that you could potentially use for your analysis.", @@ -869,7 +869,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Rotate log files on reaching 100 MB or as appropriate.", "RationaleStatement": "Kubernetes automatically rotates the log files. Retaining old log files ensures that you would have sufficient log data available for carrying out any investigation or correlation. If you have set file size of 100 MB and the number of old log files to keep as 10, you would approximate have 1 GB of log data that you could potentially use for your analysis.", @@ -892,7 +892,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Set global request timeout for API server requests as appropriate.", "RationaleStatement": "Setting global request timeout allows extending the API server request timeout limit to a duration appropriate to the user's connection speed. By default, it is set to 60 seconds which might be problematic on slower connections making cluster resources inaccessible once the data volume for requests exceeds what can be transmitted in 60 seconds. But, setting this timeout limit to be too large can exhaust the API server resources making it prone to Denial-of-Service attack. Hence, it is recommended to set this limit as appropriate and change the default limit of 60 seconds only if needed.", @@ -915,7 +915,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Validate service account before validating token.", "RationaleStatement": "If `--service-account-lookup` is not enabled, the apiserver only verifies that the authentication token is valid, and does not validate that the service account token mentioned in the request is actually present in etcd. This allows using a service account token even after the corresponding service account is deleted. This is an example of time of check to time of use security issue.", @@ -938,7 +938,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Explicitly set a service account public key file for service accounts on the apiserver.", "RationaleStatement": "By default, if no `--service-account-key-file` is specified to the apiserver, it uses the private key from the TLS serving certificate to verify service account tokens. To ensure that the keys for service account tokens could be rotated as needed, a separate public/private key pair should be used for signing service account tokens. Hence, the public key should be specified to the apiserver with `--service-account-key-file`.", @@ -961,7 +961,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured to make use of TLS encryption for client connections.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be protected by client authentication. This requires the API server to identify itself to the etcd server using a client certificate and key.", @@ -984,7 +984,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Setup TLS connection on the API server.", "RationaleStatement": "API server communication contains sensitive parameters that should remain encrypted in transit. Configure the API server to serve only HTTPS traffic.", @@ -1007,7 +1007,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Setup TLS connection on the API server.", "RationaleStatement": "API server communication contains sensitive parameters that should remain encrypted in transit. Configure the API server to serve only HTTPS traffic. If `--client-ca-file` argument is set, any request presenting a client certificate signed by one of the authorities in the `client-ca-file` is authenticated with an identity corresponding to the CommonName of the client certificate.", @@ -1030,7 +1030,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured to make use of TLS encryption for client connections.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be protected by client authentication. This requires the API server to identify itself to the etcd server using a SSL Certificate Authority file.", @@ -1053,7 +1053,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Encrypt etcd key-value store.", "RationaleStatement": "etcd is a highly available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be encrypted at rest to avoid any disclosures.", @@ -1074,7 +1074,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Where `etcd` encryption is used, appropriate providers should be configured.", "RationaleStatement": "Where `etcd` encryption is used, it is important to ensure that the appropriate set of encryption providers is used. Currently, the `aescbc`, `kms`, and `secretbox` are likely to be appropriate options.", @@ -1097,7 +1097,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the API server is configured to only use strong cryptographic ciphers.", "RationaleStatement": "TLS ciphers have had a number of known vulnerabilities and weaknesses, which can reduce the protection provided by them. By default Kubernetes supports a number of TLS cipher suites including some that have security concerns, weakening the protection provided.", @@ -1118,7 +1118,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "By default Kubernetes extends service account token lifetimes to one year to aid in transition from the legacy token settings.", "RationaleStatement": "This default setting is not ideal for security as it ignores other settings related to maximum token lifetime and means that a lost or stolen credential could be valid for an extended period of time.", @@ -1141,7 +1141,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Activate garbage collector on pod termination, as appropriate.", "RationaleStatement": "Garbage collection is important to ensure sufficient resource availability and avoiding degraded performance and availability. In the worst case, the system might crash or just be unusable for a long period of time. The current setting for garbage collection is 12,500 terminated pods which might be too high for your system to sustain. Based on your system resources and tests, choose an appropriate threshold value to activate garbage collection.", @@ -1164,7 +1164,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable profiling, if not needed.", "RationaleStatement": "Profiling allows for the identification of specific performance bottlenecks. It generates a significant amount of program data that could potentially be exploited to uncover system and program details. If you are not experiencing any bottlenecks and do not need the profiler for troubleshooting purposes, it is recommended to turn it off to reduce the potential attack surface.", @@ -1187,7 +1187,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Use individual service account credentials for each controller.", "RationaleStatement": "The controller manager creates a service account per controller in the `kube-system` namespace, generates a credential for it, and builds a dedicated API client with that service account credential for each controller loop to use. Setting the `--use-service-account-credentials` to `true` runs each control loop within the controller manager using a separate service account credential. When used in combination with RBAC, this ensures that the control loops run with the minimum permissions required to perform their intended tasks.", @@ -1210,7 +1210,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Explicitly set a service account private key file for service accounts on the controller manager.", "RationaleStatement": "To ensure that keys for service account tokens can be rotated as needed, a separate public/private key pair should be used for signing service account tokens. The private key should be specified to the controller manager with `--service-account-private-key-file` as appropriate.", @@ -1233,7 +1233,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Allow pods to verify the API server's serving certificate before establishing connections.", "RationaleStatement": "Processes running within pods that need to contact the API server must verify the API server's serving certificate. Failing to do so could be a subject to man-in-the-middle attacks. Providing the root certificate for the API server's serving certificate to the controller manager with the `--root-ca-file` argument allows the controller manager to inject the trusted bundle into pods so that they can verify TLS connections to the API server.", @@ -1256,7 +1256,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable kubelet server certificate rotation on controller-manager.", "RationaleStatement": "`RotateKubeletServerCertificate` causes the kubelet to both request a serving certificate after bootstrapping its client credentials and rotate the certificate as its existing credentials expire. This automated periodic rotation ensures that the there are no downtimes due to expired certificates and thus addressing availability in the CIA security triad. Note: This recommendation only applies if you let kubelets get their certificates from the API server. In case your kubelet certificates come from an outside authority/tool (e.g. Vault) then you need to take care of rotation yourself.", @@ -1279,7 +1279,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not bind the Controller Manager service to non-loopback insecure addresses.", "RationaleStatement": "The Controller Manager API service which runs on port 10252/TCP by default is used for health and metrics information and is available without authentication or encryption. As such it should only be bound to a localhost interface, to minimize the cluster's attack surface", @@ -1302,7 +1302,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.4 Scheduler", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable profiling, if not needed.", "RationaleStatement": "Profiling allows for the identification of specific performance bottlenecks. It generates a significant amount of program data that could potentially be exploited to uncover system and program details. If you are not experiencing any bottlenecks and do not need the profiler for troubleshooting purposes, it is recommended to turn it off to reduce the potential attack surface.", @@ -1325,7 +1325,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.4 Scheduler", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not bind the scheduler service to non-loopback insecure addresses.", "RationaleStatement": "The Scheduler API service which runs on port 10251/TCP by default is used for health and metrics information and is available without authentication or encryption. As such it should only be bound to a localhost interface, to minimize the cluster's attack surface", @@ -1347,7 +1347,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Configure TLS encryption for the etcd service.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be encrypted in transit.", @@ -1369,7 +1369,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Configure TLS encryption for the etcd service.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be encrypted in transit.", @@ -1391,7 +1391,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable client authentication on etcd service.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should not be available to unauthenticated clients. You should enable the client authentication via valid certificates to secure the access to the etcd service.", @@ -1413,7 +1413,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not use self-signed certificates for TLS.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should not be available to unauthenticated clients. You should enable the client authentication via valid certificates to secure the access to the etcd service.", @@ -1435,7 +1435,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured to make use of TLS encryption for peer connections.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be encrypted in transit and also amongst peers in the etcd clusters.", @@ -1457,7 +1457,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured for peer authentication.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be accessible only by authenticated etcd peers in the etcd cluster.", @@ -1479,7 +1479,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not use automatically generated self-signed certificates for TLS connections between peers.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be accessible only by authenticated etcd peers in the etcd cluster. Hence, do not use self-signed certificates for authentication.", @@ -1522,7 +1522,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.1 Authentication and Authorization", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes provides the option to use client certificates for user authentication. However as there is no way to revoke these certificates when a user leaves an organization or loses their credential, they are not suitable for this purpose. It is not possible to fully disable client certificate use within a cluster as it is used for component to component authentication.", "RationaleStatement": "With any authentication mechanism the ability to revoke credentials if they are compromised or no longer required, is a key control. Kubernetes client certificate authentication does not allow for this due to a lack of support for certificate revocation.", @@ -1543,7 +1543,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.1 Authentication and Authorization", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes provides service account tokens which are intended for use by workloads running in the Kubernetes cluster, for authentication to the API server. These tokens are not designed for use by end-users and do not provide for features such as revocation or expiry, making them insecure. A newer version of the feature (Bound service account token volumes) does introduce expiry but still does not allow for specific revocation.", "RationaleStatement": "With any authentication mechanism the ability to revoke credentials if they are compromised or no longer required, is a key control. Service account token authentication does not allow for this due to the use of JWT tokens as an underlying technology.", @@ -1564,7 +1564,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.1 Authentication and Authorization", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes provides bootstrap tokens which are intended for use by new nodes joining the cluster These tokens are not designed for use by end-users they are specifically designed for the purpose of bootstrapping new nodes and not for general authentication", "RationaleStatement": "Bootstrap tokens are not intended for use as a general authentication mechanism and impose constraints on user and group naming that do not facilitate good RBAC design. They also cannot be used with MFA resulting in a weak authentication mechanism being available.", @@ -1585,7 +1585,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.2 Logging", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes can audit the details of requests made to the API server. The `--audit-policy-file` flag must be set for this logging to be enabled.", "RationaleStatement": "Logging is an important detective control for all systems, to detect potential unauthorised access.", @@ -1629,7 +1629,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet` service file has permissions of `600` or more restrictive.", "RationaleStatement": "The `kubelet` service file controls various parameters that set the behavior of the `kubelet` service in the worker node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1652,7 +1652,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet` service file ownership is set to `root:root`.", "RationaleStatement": "The `kubelet` service file controls various parameters that set the behavior of the `kubelet` service in the worker node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1673,7 +1673,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "If `kube-proxy` is running, and if it is using a file-based kubeconfig file, ensure that the proxy kubeconfig file has permissions of `600` or more restrictive.", "RationaleStatement": "The `kube-proxy` kubeconfig file controls various parameters of the `kube-proxy` service in the worker node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system. It is possible to run `kube-proxy` with the kubeconfig parameters configured as a Kubernetes ConfigMap instead of a file. In this case, there is no proxy kubeconfig file.", @@ -1694,7 +1694,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "If `kube-proxy` is running, ensure that the file ownership of its kubeconfig file is set to `root:root`.", "RationaleStatement": "The kubeconfig file for `kube-proxy` controls various parameters for the `kube-proxy` service in the worker node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1717,7 +1717,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet.conf` file has permissions of `600` or more restrictive.", "RationaleStatement": "The `kubelet.conf` file is the kubeconfig file for the node, and controls various parameters that set the behavior and identity of the worker node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1740,7 +1740,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet.conf` file ownership is set to `root:root`.", "RationaleStatement": "The `kubelet.conf` file is the kubeconfig file for the node, and controls various parameters that set the behavior and identity of the worker node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1761,7 +1761,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the certificate authorities file has permissions of `644` or more restrictive.", "RationaleStatement": "The certificate authorities file controls the authorities used to validate API requests. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1782,7 +1782,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the certificate authorities file ownership is set to `root:root`.", "RationaleStatement": "The certificate authorities file controls the authorities used to validate API requests. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1805,7 +1805,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that if the kubelet refers to a configuration file with the `--config` argument, that file has permissions of 600 or more restrictive.", "RationaleStatement": "The kubelet reads various parameters, including security settings, from a config file specified by the `--config` argument. If this file is specified you should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1828,7 +1828,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that if the kubelet refers to a configuration file with the `--config` argument, that file is owned by root:root.", "RationaleStatement": "The kubelet reads various parameters, including security settings, from a config file specified by the `--config` argument. If this file is specified you should restrict its file permissions to maintain the integrity of the file. The file should be owned by root:root.", @@ -1851,7 +1851,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable anonymous requests to the Kubelet server.", "RationaleStatement": "When enabled, requests that are not rejected by other configured authentication methods are treated as anonymous requests. These requests are then served by the Kubelet server. You should rely on authentication to authorize access and disallow anonymous requests.", @@ -1874,7 +1874,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not allow all requests. Enable explicit authorization.", "RationaleStatement": "Kubelets, by default, allow all authenticated requests (even anonymous ones) without needing explicit authorization checks from the apiserver. You should restrict this behavior and only allow explicitly authorized requests.", @@ -1897,7 +1897,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable Kubelet authentication using certificates.", "RationaleStatement": "The connections from the apiserver to the kubelet are used for fetching logs for pods, attaching (through kubectl) to running pods, and using the kubelet’s port-forwarding functionality. These connections terminate at the kubelet’s HTTPS endpoint. By default, the apiserver does not verify the kubelet’s serving certificate, which makes the connection subject to man-in-the-middle attacks, and unsafe to run over untrusted and/or public networks. Enabling Kubelet certificate authentication ensures that the apiserver could authenticate the Kubelet before submitting any requests.", @@ -1920,7 +1920,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Disable the read-only port.", "RationaleStatement": "The Kubelet process provides a read-only API in addition to the main Kubelet API. Unauthenticated access is provided to this read-only API which could possibly retrieve potentially sensitive information about the cluster.", @@ -1943,7 +1943,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not disable timeouts on streaming connections.", "RationaleStatement": "Setting idle timeouts ensures that you are protected against Denial-of-Service attacks, inactive connections and running out of ephemeral ports. **Note:** By default, `--streaming-connection-idle-timeout` is set to 4 hours which might be too high for your environment. Setting this as appropriate would additionally ensure that such streaming connections are timed out after serving legitimate use cases.", @@ -1966,7 +1966,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Allow Kubelet to manage iptables.", "RationaleStatement": "Kubelets can automatically manage the required changes to iptables based on how you choose your networking options for the pods. It is recommended to let kubelets manage the changes to iptables. This ensures that the iptables configuration remains in sync with pods networking configuration. Manually configuring iptables with dynamic pod network configuration changes might hamper the communication between pods/containers and to the outside world. You might have iptables rules too restrictive or too open.", @@ -1987,7 +1987,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not override node hostnames.", "RationaleStatement": "Overriding hostnames could potentially break TLS setup between the kubelet and the apiserver. Additionally, with overridden hostnames, it becomes increasingly difficult to associate logs with a particular node and process them for security analytics. Hence, you should setup your kubelet nodes with resolvable FQDNs and avoid overriding the hostnames with IPs.", @@ -2033,7 +2033,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Setup TLS connection on the Kubelets.", "RationaleStatement": "The connections from the apiserver to the kubelet are used for fetching logs for pods, attaching (through kubectl) to running pods, and using the kubelet’s port-forwarding functionality. These connections terminate at the kubelet’s HTTPS endpoint. By default, the apiserver does not verify the kubelet’s serving certificate, which makes the connection subject to man-in-the-middle attacks, and unsafe to run over untrusted and/or public networks.", @@ -2056,7 +2056,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable kubelet client certificate rotation.", "RationaleStatement": "The `--rotate-certificates` setting causes the kubelet to rotate its client certificates by creating new CSRs as its existing credentials expire. This automated periodic rotation ensures that the there is no downtime due to expired certificates and thus addressing availability in the CIA security triad. **Note:** This recommendation only applies if you let kubelets get their certificates from the API server. In case your kubelet certificates come from an outside authority/tool (e.g. Vault) then you need to take care of rotation yourself. **Note:** This feature also require the `RotateKubeletClientCertificate` feature gate to be enabled (which is the default since Kubernetes v1.7)", @@ -2077,7 +2077,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Enable kubelet server certificate rotation.", "RationaleStatement": "`RotateKubeletServerCertificate` causes the kubelet to both request a serving certificate after bootstrapping its client credentials and rotate the certificate as its existing credentials expire. This automated periodic rotation ensures that the there are no downtimes due to expired certificates and thus addressing availability in the CIA security triad. Note: This recommendation only applies if you let kubelets get their certificates from the API server. In case your kubelet certificates come from an outside authority/tool (e.g. Vault) then you need to take care of rotation yourself.", @@ -2100,7 +2100,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Kubelet is configured to only use strong cryptographic ciphers.", "RationaleStatement": "TLS ciphers have had a number of known vulnerabilities and weaknesses, which can reduce the protection provided by them. By default Kubernetes supports a number of TLS ciphersuites including some that have security concerns, weakening the protection provided.", @@ -2121,7 +2121,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Kubelet sets limits on the number of PIDs that can be created by pods running on the node.", "RationaleStatement": "By default pods running in a cluster can consume any number of PIDs, potentially exhausting the resources available on the node. Setting an appropriate limit reduces the risk of a denial of service attack on cluster nodes.", @@ -2142,7 +2142,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Kubelet enforces the use of the RuntimeDefault seccomp profile", "RationaleStatement": "By default, Kubernetes disables the seccomp profile which ships with most container runtimes. Setting this parameter will ensure workloads running on the node are protected by the runtime's seccomp profile.", @@ -2184,7 +2184,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.3 kube-proxy", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not bind the kube-proxy metrics port to non-loopback addresses.", "RationaleStatement": "kube-proxy has two APIs which provided access to information about the service and can be bound to network ports. The metrics API service includes endpoints (`/metrics` and `/configz`) which disclose information about the configuration and operation of kube-proxy. These endpoints should not be exposed to untrusted networks as they do not support encryption or authentication to restrict access to the data they provide.", @@ -2207,7 +2207,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The RBAC role `cluster-admin` provides wide-ranging powers over the environment and should be used only where and when needed.", "RationaleStatement": "Kubernetes provides a set of default roles where RBAC is used. Some of these roles such as `cluster-admin` provide wide-ranging privileges which should only be applied where absolutely necessary. Roles such as `cluster-admin` allow super-user access to perform any action on any resource. When used in a `ClusterRoleBinding`, it gives full control over every resource in the cluster and in all namespaces. When used in a `RoleBinding`, it gives full control over every resource in the rolebinding's namespace, including the namespace itself.", @@ -2230,7 +2230,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The Kubernetes API stores secrets, which may be service account tokens for the Kubernetes API or credentials used by workloads in the cluster. Access to these secrets should be restricted to the smallest possible group of users to reduce the risk of privilege escalation.", "RationaleStatement": "Inappropriate access to secrets stored within the Kubernetes cluster can allow for an attacker to gain additional access to the Kubernetes cluster or external resources whose credentials are stored as secrets.", @@ -2253,7 +2253,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes Roles and ClusterRoles provide access to resources based on sets of objects and actions that can be taken on those objects. It is possible to set either of these to be the wildcard * which matches all items. Use of wildcards is not optimal from a security perspective as it may allow for inadvertent access to be granted when new resources are added to the Kubernetes API either as CRDs or in later versions of the product.", "RationaleStatement": "The principle of least privilege recommends that users are provided only the access required for their role and nothing more. The use of wildcard rights grants is likely to provide excessive rights to the Kubernetes API.", @@ -2276,7 +2276,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The ability to create pods in a namespace can provide a number of opportunities for privilege escalation, such as assigning privileged service accounts to these pods or mounting hostPaths with access to sensitive data (unless Pod Security Policies are implemented to restrict this access) As such, access to create new pods should be restricted to the smallest possible group of users.", "RationaleStatement": "The ability to create pods in a cluster opens up possibilities for privilege escalation and should be restricted, where possible.", @@ -2297,7 +2297,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The `default` service account should not be used to ensure that rights granted to applications can be more easily audited and reviewed.", "RationaleStatement": "Kubernetes provides a default service account which is used by cluster workloads where no specific service account is assigned to the pod. Where access to the Kubernetes API from a pod is required, a specific service account should be created for that pod, and rights granted to that service account. The default service account should be configured to ensure that it does not automatically provide a service account token, and it must not have any non-default role bindings or custom role assignments", @@ -2318,7 +2318,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Service accounts tokens should not be mounted in pods except where the workload running in the pod explicitly needs to communicate with the API server", "RationaleStatement": "Mounting service account tokens inside pods can provide an avenue for privilege escalation attacks where an attacker is able to compromise a single pod in the cluster. Avoiding mounting these tokens removes this attack avenue.", @@ -2339,7 +2339,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The special group `system:masters` should not be used to grant permissions to any user or service account, except where strictly necessary (e.g. bootstrapping access prior to RBAC being fully available)", "RationaleStatement": "The `system:masters` group has unrestricted access to the Kubernetes API hard-coded into the API server source code. An authenticated user who is a member of this group cannot have their access reduced, even if all bindings and cluster role bindings which mention it, are removed. When combined with client certificate authentication, use of this group can allow for irrevocable cluster-admin level credentials to exist for a cluster.", @@ -2360,7 +2360,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Cluster roles and roles with the impersonate, bind or escalate permissions should not be granted unless strictly required. Each of these permissions allow a particular subject to escalate their privileges beyond those explicitly granted by cluster administrators", "RationaleStatement": "The impersonate privilege allows a subject to impersonate other users gaining their rights to the cluster. The bind privilege allows the subject to add a binding to a cluster role or role which escalates their effective permissions in the cluster. The escalate privilege allows a subject to modify cluster roles to which they are bound, increasing their rights to that level. Each of these permissions has the potential to allow for privilege escalation to cluster-admin level.", @@ -2383,7 +2383,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The ability to create persistent volumes in a cluster can provide an opportunity for privilege escalation, via the creation of `hostPath` volumes. As persistent volumes are not covered by Pod Security Admission, a user with access to create persistent volumes may be able to get access to sensitive files from the underlying host even where restrictive Pod Security Admission policies are in place.", "RationaleStatement": "The ability to create persistent volumes in a cluster opens up possibilities for privilege escalation and should be restricted, where possible.", @@ -2406,7 +2406,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with access to the `Proxy` sub-resource of `Node` objects automatically have permissions to use the kubelet API, which may allow for privilege escalation or bypass cluster security controls such as audit logs. The kubelet provides an API which includes rights to execute commands in any container running on the node. Access to this API is covered by permissions to the main Kubernetes API via the `node` object. The proxy sub-resource specifically allows wide ranging access to the kubelet API. Direct access to the kubelet API bypasses controls like audit logging (there is no audit log of kubelet API access) and admission control.", "RationaleStatement": "The ability to use the `proxy` sub-resource of `node` objects opens up possibilities for privilege escalation and should be restricted, where possible.", @@ -2429,7 +2429,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with access to the update the `approval` sub-resource of `CertificateSigningRequests` objects can approve new client certificates for the Kubernetes API effectively allowing them to create new high-privileged user accounts. This can allow for privilege escalation to full cluster administrator, depending on users configured in the cluster", "RationaleStatement": "The ability to update certificate signing requests should be limited.", @@ -2452,7 +2452,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with rights to create/modify/delete `validatingwebhookconfigurations` or `mutatingwebhookconfigurations` can control webhooks that can read any object admitted to the cluster, and in the case of mutating webhooks, also mutate admitted objects. This could allow for privilege escalation or disruption of the operation of the cluster.", "RationaleStatement": "The ability to manage webhook configuration should be limited", @@ -2475,7 +2475,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with rights to create new service account tokens at a cluster level, can create long-lived privileged credentials in the cluster. This could allow for privilege escalation and persistent access to the cluster, even if the users account has been revoked.", "RationaleStatement": "The ability to create service account tokens should be limited.", @@ -2496,7 +2496,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Every Kubernetes cluster should have at least one policy control mechanism in place to enforce the other requirements in this section. This could be the in-built Pod Security Admission controller, or a third party policy control system.", "RationaleStatement": "Without an active policy control mechanism, it is not possible to limit the use of containers with access to underlying cluster nodes, via mechanisms like privileged containers, or the use of hostPath volume mounts.", @@ -2519,7 +2519,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `securityContext.privileged` flag set to `true`.", "RationaleStatement": "Privileged containers have access to all Linux Kernel capabilities and devices. A container running with full privileges can do almost everything that the host can do. This flag exists to allow special use-cases, like manipulating the network stack and accessing devices. There should be at least one admission control policy defined which does not permit privileged containers. If you need to run privileged containers, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2542,7 +2542,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `hostPID` flag set to true.", "RationaleStatement": "A container running in the host's PID namespace can inspect processes running outside the container. If the container also has access to ptrace capabilities this can be used to escalate privileges outside of the container. There should be at least one admission control policy defined which does not permit containers to share the host PID namespace. If you need to run containers which require hostPID, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2565,7 +2565,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `hostIPC` flag set to true.", "RationaleStatement": "A container running in the host's IPC namespace can use IPC to interact with processes outside the container. There should be at least one admission control policy defined which does not permit containers to share the host IPC namespace. If you need to run containers which require hostIPC, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2588,7 +2588,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `hostNetwork` flag set to true.", "RationaleStatement": "A container running in the host's network namespace could access the local loopback device, and could access network traffic to and from other pods. There should be at least one admission control policy defined which does not permit containers to share the host network namespace. If you need to run containers which require access to the host's network namespaces, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2611,7 +2611,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `allowPrivilegeEscalation` flag set to true. Allowing this right can lead to a process running a container getting more rights than it started with. It's important to note that these rights are still constrained by the overall container sandbox, and this setting does not relate to the use of privileged containers.", "RationaleStatement": "A container running with the `allowPrivilegeEscalation` flag set to `true` may have processes that can gain more privileges than their parent. There should be at least one admission control policy defined which does not permit containers to allow privilege escalation. The option exists (and is defaulted to true) to permit setuid binaries to run. If you have need to run containers which use setuid binaries or require privilege escalation, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2657,7 +2657,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers with the potentially dangerous NET_RAW capability.", "RationaleStatement": "Containers run with a default set of capabilities as assigned by the Container Runtime. By default this can include potentially dangerous capabilities. With Docker as the container runtime the NET_RAW capability is enabled which may be misused by malicious containers. Ideally, all containers should drop this capability. There should be at least one admission control policy defined which does not permit containers with the NET_RAW capability. If you need to run containers with this capability, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2680,7 +2680,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers with capabilities assigned beyond the default set.", "RationaleStatement": "Containers run with a default set of capabilities as assigned by the Container Runtime. Capabilities outside this set can be added to containers which could expose them to risks of container breakout attacks. There should be at least one policy defined which prevents containers with capabilities beyond the default set from launching. If you need to run containers with additional capabilities, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2726,7 +2726,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit Windows containers to be run with the `hostProcess` flag set to true.", "RationaleStatement": "A Windows container making use of the `hostProcess` flag can interact with the underlying Windows cluster node. As per the Kubernetes documentation, this provides privileged access to the Windows node. Where Windows containers are used inside a Kubernetes cluster, there should be at least one admission control policy which does not permit `hostProcess` Windows containers. If you need to run Windows containers which require `hostProcess`, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2747,7 +2747,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally admit containers which make use of `hostPath` volumes.", "RationaleStatement": "A container which mounts a `hostPath` volume as part of its specification will have access to the filesystem of the underlying cluster node. The use of `hostPath` volumes may allow containers access to privileged areas of the node filesystem. There should be at least one admission control policy defined which does not permit containers to mount `hostPath` volumes. If you need to run containers which require `hostPath` volumes, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2770,7 +2770,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers which require the use of HostPorts.", "RationaleStatement": "Host ports connect containers directly to the host's network. This can bypass controls such as network policy. There should be at least one admission control policy defined which does not permit containers which require the use of HostPorts. If you need to run containers which require HostPorts, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2791,7 +2791,7 @@ { "Section": "5 Policies", "SubSection": "5.3 Network Policies and CNI", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "There are a variety of CNI plugins available for Kubernetes. If the CNI in use does not support Network Policies it may not be possible to effectively restrict traffic in the cluster.", "RationaleStatement": "Kubernetes network policies are enforced by the CNI plugin in use. As such it is important to ensure that the CNI plugin supports both Ingress and Egress network policies.", @@ -2898,7 +2898,7 @@ { "Section": "5 Policies", "SubSection": "5.6 General Policies", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Use namespaces to isolate your Kubernetes objects.", "RationaleStatement": "Limiting the scope of user permissions can reduce the impact of mistakes or malicious activities. A Kubernetes namespace allows you to partition created resources into logically named groups. Resources created in one namespace can be hidden from other namespaces. By default, each resource created by a user in Kubernetes cluster runs in a default namespace, called `default`. You can create additional namespaces and attach resources and users to them. You can use Kubernetes Authorization plugins to create policies that segregate access to namespace resources between different users.", diff --git a/prowler/compliance/kubernetes/cis_1.8_kubernetes.json b/prowler/compliance/kubernetes/cis_1.8_kubernetes.json index 88fcfd59b2..af8d9e6c8a 100644 --- a/prowler/compliance/kubernetes/cis_1.8_kubernetes.json +++ b/prowler/compliance/kubernetes/cis_1.8_kubernetes.json @@ -12,7 +12,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the API server pod specification file has permissions of `600` or more restrictive.", "RationaleStatement": "The API server pod specification file controls various parameters that set the behavior of the API server. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -33,7 +33,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the API server pod specification file ownership is set to `root:root`.", "RationaleStatement": "The API server pod specification file controls various parameters that set the behavior of the API server. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -54,7 +54,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.", "RationaleStatement": "The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -75,7 +75,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the controller manager pod specification file ownership is set to `root:root`.", "RationaleStatement": "The controller manager pod specification file controls various parameters that set the behavior of various components of the master node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -96,7 +96,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the scheduler pod specification file has permissions of `600` or more restrictive.", "RationaleStatement": "The scheduler pod specification file controls various parameters that set the behavior of the Scheduler service in the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -117,7 +117,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the scheduler pod specification file ownership is set to `root:root`.", "RationaleStatement": "The scheduler pod specification file controls various parameters that set the behavior of the `kube-scheduler` service in the master node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -138,7 +138,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `/etc/kubernetes/manifests/etcd.yaml` file has permissions of `600` or more restrictive.", "RationaleStatement": "The etcd pod specification file `/etc/kubernetes/manifests/etcd.yaml` controls various parameters that set the behavior of the `etcd` service in the master node. etcd is a highly-available key-value store which Kubernetes uses for persistent storage of all of its REST API object. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -159,7 +159,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `/etc/kubernetes/manifests/etcd.yaml` file ownership is set to `root:root`.", "RationaleStatement": "The etcd pod specification file `/etc/kubernetes/manifests/etcd.yaml` controls various parameters that set the behavior of the `etcd` service in the master node. etcd is a highly-available key-value store which Kubernetes uses for persistent storage of all of its REST API object. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -180,7 +180,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Container Network Interface files have permissions of `600` or more restrictive.", "RationaleStatement": "Container Network Interface provides various networking options for overlay networking. You should consult their documentation and restrict their respective file permissions to maintain the integrity of those files. Those files should be writable by only the administrators on the system.", @@ -201,7 +201,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Container Network Interface files have ownership set to `root:root`.", "RationaleStatement": "Container Network Interface provides various networking options for overlay networking. You should consult their documentation and restrict their respective file permissions to maintain the integrity of those files. Those files should be owned by `root:root`.", @@ -222,7 +222,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the etcd data directory has permissions of `700` or more restrictive.", "RationaleStatement": "etcd is a highly-available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. This data directory should be protected from any unauthorized reads or writes. It should not be readable or writable by any group members or the world.", @@ -243,7 +243,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the etcd data directory ownership is set to `etcd:etcd`.", "RationaleStatement": "etcd is a highly-available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. This data directory should be protected from any unauthorized reads or writes. It should be owned by `etcd:etcd`.", @@ -264,7 +264,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `admin.conf` file has permissions of `600`.", "RationaleStatement": "The `admin.conf` is the administrator kubeconfig file defining various settings for the administration of the cluster. This file contains private key and respective certificate allowed to fully manage the cluster. You should restrict its file permissions to maintain the integrity and confidentiality of the file. The file should be readable and writable by only the administrators on the system.", @@ -285,7 +285,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `admin.conf` file ownership is set to `root:root`.", "RationaleStatement": "The `admin.conf` file contains the admin credentials for the cluster. You should set its file ownership to maintain the integrity and confidentiality of the file. The file should be owned by root:root.", @@ -306,7 +306,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `scheduler.conf` file has permissions of `600` or more restrictive.", "RationaleStatement": "The `scheduler.conf` file is the kubeconfig file for the Scheduler. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -327,7 +327,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `scheduler.conf` file ownership is set to `root:root`.", "RationaleStatement": "The `scheduler.conf` file is the kubeconfig file for the Scheduler. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -348,7 +348,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `controller-manager.conf` file has permissions of 600 or more restrictive.", "RationaleStatement": "The `controller-manager.conf` file is the kubeconfig file for the Controller Manager. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -369,7 +369,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `controller-manager.conf` file ownership is set to `root:root`.", "RationaleStatement": "The `controller-manager.conf` file is the kubeconfig file for the Controller Manager. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -390,7 +390,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the Kubernetes PKI directory and file ownership is set to `root:root`.", "RationaleStatement": "Kubernetes makes use of a number of certificates as part of its operation. You should set the ownership of the directory containing the PKI information and all files in that directory to maintain their integrity. The directory and files should be owned by `root:root`.", @@ -411,7 +411,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that Kubernetes PKI certificate files have permissions of `600` or more restrictive.", "RationaleStatement": "Kubernetes makes use of a number of certificate files as part of the operation of its components. The permissions on these files should be set to `600` or more restrictive to protect their integrity.", @@ -432,7 +432,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.1 Control Plane Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that Kubernetes PKI key files have permissions of `600`.", "RationaleStatement": "Kubernetes makes use of a number of key files as part of the operation of its components. The permissions on these files should be set to `600` to protect their integrity and confidentiality.", @@ -455,7 +455,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Disable anonymous requests to the API server.", "RationaleStatement": "When enabled, requests that are not rejected by other configured authentication methods are treated as anonymous requests. These requests are then served by the API server. You should rely on authentication to authorize access and disallow anonymous requests. If you are using RBAC authorization, it is generally considered reasonable to allow anonymous access to the API Server for health checks and discovery purposes, and hence this recommendation is not scored. However, you should consider whether anonymous discovery is an acceptable risk for your purposes.", @@ -478,7 +478,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not use token based authentication.", "RationaleStatement": "The token-based authentication utilizes static tokens to authenticate requests to the apiserver. The tokens are stored in clear-text in a file on the apiserver, and cannot be revoked or rotated without restarting the apiserver. Hence, do not use static token-based authentication.", @@ -501,7 +501,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "This admission controller rejects all net-new usage of the Service field externalIPs.", "RationaleStatement": "Most users do not need the ability to set the `externalIPs` field for a `Service` at all, and cluster admins should consider disabling this functionality by enabling the `DenyServiceExternalIPs` admission controller. Clusters that do need to allow this functionality should consider using some custom policy to manage its usage.", @@ -524,7 +524,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable certificate based kubelet authentication.", "RationaleStatement": "The apiserver, by default, does not authenticate itself to the kubelet's HTTPS endpoints. The requests from the apiserver are treated anonymously. You should set up certificate-based kubelet authentication to ensure that the apiserver authenticates itself to kubelets when submitting requests.", @@ -547,7 +547,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Verify kubelet's certificate before establishing connection.", "RationaleStatement": "The connections from the apiserver to the kubelet are used for fetching logs for pods, attaching (through kubectl) to running pods, and using the kubelet’s port-forwarding functionality. These connections terminate at the kubelet’s HTTPS endpoint. By default, the apiserver does not verify the kubelet’s serving certificate, which makes the connection subject to man-in-the-middle attacks, and unsafe to run over untrusted and/or public networks.", @@ -570,7 +570,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not always authorize all requests.", "RationaleStatement": "The API Server, can be configured to allow all requests. This mode should not be used on any production cluster.", @@ -593,7 +593,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Restrict kubelet nodes to reading only objects associated with them.", "RationaleStatement": "The `Node` authorization mode only allows kubelets to read `Secret`, `ConfigMap`, `PersistentVolume`, and `PersistentVolumeClaim` objects associated with their nodes.", @@ -616,7 +616,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Turn on Role Based Access Control.", "RationaleStatement": "Role Based Access Control (RBAC) allows fine-grained control over the operations that different entities can perform on different objects in the cluster. It is recommended to use the RBAC authorization mode.", @@ -639,7 +639,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Limit the rate at which the API server accepts requests.", "RationaleStatement": "Using `EventRateLimit` admission control enforces a limit on the number of events that the API Server will accept in a given time slice. A misbehaving workload could overwhelm and DoS the API Server, making it unavailable. This particularly applies to a multi-tenant cluster, where there might be a small percentage of misbehaving tenants which could have a significant impact on the performance of the cluster overall. Hence, it is recommended to limit the rate of events that the API server will accept. Note: This is an Alpha feature in the Kubernetes 1.15 release.", @@ -662,7 +662,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not allow all requests.", "RationaleStatement": "Setting admission control plugin `AlwaysAdmit` allows all requests and do not filter any requests. The `AlwaysAdmit` admission controller was deprecated in Kubernetes v1.13. Its behavior was equivalent to turning off all admission controllers.", @@ -685,7 +685,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Always pull images.", "RationaleStatement": "Setting admission control policy to `AlwaysPullImages` forces every new pod to pull the required images every time. In a multi-tenant cluster users can be assured that their private images can only be used by those who have the credentials to pull them. Without this admission control policy, once an image has been pulled to a node, any pod from any user can use it simply by knowing the image’s name, without any authorization check against the image ownership. When this plug-in is enabled, images are always pulled prior to starting containers, which means valid credentials are required.", @@ -800,7 +800,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable profiling, if not needed.", "RationaleStatement": "Profiling allows for the identification of specific performance bottlenecks. It generates a significant amount of program data that could potentially be exploited to uncover system and program details. If you are not experiencing any bottlenecks and do not need the profiler for troubleshooting purposes, it is recommended to turn it off to reduce the potential attack surface.", @@ -823,7 +823,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable auditing on the Kubernetes API Server and set the desired audit log path.", "RationaleStatement": "Auditing the Kubernetes API Server provides a security-relevant chronological set of records documenting the sequence of activities that have affected system by individual users, administrators or other components of the system. Even though currently, Kubernetes provides only basic audit capabilities, it should be enabled. You can enable it by setting an appropriate audit log path.", @@ -846,7 +846,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Retain the logs for at least 30 days or as appropriate.", "RationaleStatement": "Retaining logs for at least 30 days ensures that you can go back in time and investigate or correlate any events. Set your audit log retention period to 30 days or as per your business requirements.", @@ -869,7 +869,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Retain 10 or an appropriate number of old log files.", "RationaleStatement": "Kubernetes automatically rotates the log files. Retaining old log files ensures that you would have sufficient log data available for carrying out any investigation or correlation. For example, if you have set file size of 100 MB and the number of old log files to keep as 10, you would approximate have 1 GB of log data that you could potentially use for your analysis.", @@ -892,7 +892,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Rotate log files on reaching 100 MB or as appropriate.", "RationaleStatement": "Kubernetes automatically rotates the log files. Retaining old log files ensures that you would have sufficient log data available for carrying out any investigation or correlation. If you have set file size of 100 MB and the number of old log files to keep as 10, you would approximate have 1 GB of log data that you could potentially use for your analysis.", @@ -915,7 +915,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Set global request timeout for API server requests as appropriate.", "RationaleStatement": "Setting global request timeout allows extending the API server request timeout limit to a duration appropriate to the user's connection speed. By default, it is set to 60 seconds which might be problematic on slower connections making cluster resources inaccessible once the data volume for requests exceeds what can be transmitted in 60 seconds. But, setting this timeout limit to be too large can exhaust the API server resources making it prone to Denial-of-Service attack. Hence, it is recommended to set this limit as appropriate and change the default limit of 60 seconds only if needed.", @@ -938,7 +938,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Validate service account before validating token.", "RationaleStatement": "If `--service-account-lookup` is not enabled, the apiserver only verifies that the authentication token is valid, and does not validate that the service account token mentioned in the request is actually present in etcd. This allows using a service account token even after the corresponding service account is deleted. This is an example of time of check to time of use security issue.", @@ -961,7 +961,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Explicitly set a service account public key file for service accounts on the apiserver.", "RationaleStatement": "By default, if no `--service-account-key-file` is specified to the apiserver, it uses the private key from the TLS serving certificate to verify service account tokens. To ensure that the keys for service account tokens could be rotated as needed, a separate public/private key pair should be used for signing service account tokens. Hence, the public key should be specified to the apiserver with `--service-account-key-file`.", @@ -984,7 +984,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured to make use of TLS encryption for client connections.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be protected by client authentication. This requires the API server to identify itself to the etcd server using a client certificate and key.", @@ -1007,7 +1007,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Setup TLS connection on the API server.", "RationaleStatement": "API server communication contains sensitive parameters that should remain encrypted in transit. Configure the API server to serve only HTTPS traffic.", @@ -1030,7 +1030,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Setup TLS connection on the API server.", "RationaleStatement": "API server communication contains sensitive parameters that should remain encrypted in transit. Configure the API server to serve only HTTPS traffic. If `--client-ca-file` argument is set, any request presenting a client certificate signed by one of the authorities in the `client-ca-file` is authenticated with an identity corresponding to the CommonName of the client certificate.", @@ -1053,7 +1053,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured to make use of TLS encryption for client connections.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be protected by client authentication. This requires the API server to identify itself to the etcd server using a SSL Certificate Authority file.", @@ -1076,7 +1076,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Encrypt etcd key-value store.", "RationaleStatement": "etcd is a highly available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be encrypted at rest to avoid any disclosures.", @@ -1097,7 +1097,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Where `etcd` encryption is used, appropriate providers should be configured.", "RationaleStatement": "Where `etcd` encryption is used, it is important to ensure that the appropriate set of encryption providers is used. Currently, the `aescbc`, `kms` and `secretbox` are likely to be appropriate options.", @@ -1120,7 +1120,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the API server is configured to only use strong cryptographic ciphers.", "RationaleStatement": "TLS ciphers have had a number of known vulnerabilities and weaknesses, which can reduce the protection provided by them. By default Kubernetes supports a number of TLS ciphersuites including some that have security concerns, weakening the protection provided.", @@ -1143,7 +1143,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Activate garbage collector on pod termination, as appropriate.", "RationaleStatement": "Garbage collection is important to ensure sufficient resource availability and avoiding degraded performance and availability. In the worst case, the system might crash or just be unusable for a long period of time. The current setting for garbage collection is 12,500 terminated pods which might be too high for your system to sustain. Based on your system resources and tests, choose an appropriate threshold value to activate garbage collection.", @@ -1166,7 +1166,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable profiling, if not needed.", "RationaleStatement": "Profiling allows for the identification of specific performance bottlenecks. It generates a significant amount of program data that could potentially be exploited to uncover system and program details. If you are not experiencing any bottlenecks and do not need the profiler for troubleshooting purposes, it is recommended to turn it off to reduce the potential attack surface.", @@ -1189,7 +1189,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Use individual service account credentials for each controller.", "RationaleStatement": "The controller manager creates a service account per controller in the `kube-system` namespace, generates a credential for it, and builds a dedicated API client with that service account credential for each controller loop to use. Setting the `--use-service-account-credentials` to `true` runs each control loop within the controller manager using a separate service account credential. When used in combination with RBAC, this ensures that the control loops run with the minimum permissions required to perform their intended tasks.", @@ -1212,7 +1212,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Explicitly set a service account private key file for service accounts on the controller manager.", "RationaleStatement": "To ensure that keys for service account tokens can be rotated as needed, a separate public/private key pair should be used for signing service account tokens. The private key should be specified to the controller manager with `--service-account-private-key-file` as appropriate.", @@ -1235,7 +1235,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Allow pods to verify the API server's serving certificate before establishing connections.", "RationaleStatement": "Processes running within pods that need to contact the API server must verify the API server's serving certificate. Failing to do so could be a subject to man-in-the-middle attacks. Providing the root certificate for the API server's serving certificate to the controller manager with the `--root-ca-file` argument allows the controller manager to inject the trusted bundle into pods so that they can verify TLS connections to the API server.", @@ -1258,7 +1258,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable kubelet server certificate rotation on controller-manager.", "RationaleStatement": "`RotateKubeletServerCertificate` causes the kubelet to both request a serving certificate after bootstrapping its client credentials and rotate the certificate as its existing credentials expire. This automated periodic rotation ensures that the there are no downtimes due to expired certificates and thus addressing availability in the CIA security triad. Note: This recommendation only applies if you let kubelets get their certificates from the API server. In case your kubelet certificates come from an outside authority/tool (e.g. Vault) then you need to take care of rotation yourself.", @@ -1281,7 +1281,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.3 Controller Manager", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not bind the Controller Manager service to non-loopback insecure addresses.", "RationaleStatement": "The Controller Manager API service which runs on port 10252/TCP by default is used for health and metrics information and is available without authentication or encryption. As such it should only be bound to a localhost interface, to minimize the cluster's attack surface", @@ -1304,7 +1304,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.4 Scheduler", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable profiling, if not needed.", "RationaleStatement": "Profiling allows for the identification of specific performance bottlenecks. It generates a significant amount of program data that could potentially be exploited to uncover system and program details. If you are not experiencing any bottlenecks and do not need the profiler for troubleshooting purposes, it is recommended to turn it off to reduce the potential attack surface.", @@ -1327,7 +1327,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.4 Scheduler", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not bind the scheduler service to non-loopback insecure addresses.", "RationaleStatement": "The Scheduler API service which runs on port 10251/TCP by default is used for health and metrics information and is available without authentication or encryption. As such it should only be bound to a localhost interface, to minimize the cluster's attack surface", @@ -1349,7 +1349,7 @@ "Attributes": [ { "Section": "2 Etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Configure TLS encryption for the etcd service.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be encrypted in transit.", @@ -1371,7 +1371,7 @@ "Attributes": [ { "Section": "2 Etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable client authentication on etcd service.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should not be available to unauthenticated clients. You should enable the client authentication via valid certificates to secure the access to the etcd service.", @@ -1393,7 +1393,7 @@ "Attributes": [ { "Section": "2 Etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not use self-signed certificates for TLS.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should not be available to unauthenticated clients. You should enable the client authentication via valid certificates to secure the access to the etcd service.", @@ -1415,7 +1415,7 @@ "Attributes": [ { "Section": "2 Etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured to make use of TLS encryption for peer connections.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be encrypted in transit and also amongst peers in the etcd clusters.", @@ -1437,7 +1437,7 @@ "Attributes": [ { "Section": "2 Etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "etcd should be configured for peer authentication.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be accessible only by authenticated etcd peers in the etcd cluster.", @@ -1459,7 +1459,7 @@ "Attributes": [ { "Section": "2 Etcd", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not use automatically generated self-signed certificates for TLS connections between peers.", "RationaleStatement": "etcd is a highly-available key value store used by Kubernetes deployments for persistent storage of all of its REST API objects. These objects are sensitive in nature and should be accessible only by authenticated etcd peers in the etcd cluster. Hence, do not use self-signed certificates for authentication.", @@ -1502,7 +1502,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.1 Authentication and Authorization", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes provides the option to use client certificates for user authentication. However as there is no way to revoke these certificates when a user leaves an organization or loses their credential, they are not suitable for this purpose. It is not possible to fully disable client certificate use within a cluster as it is used for component to component authentication.", "RationaleStatement": "With any authentication mechanism the ability to revoke credentials if they are compromised or no longer required, is a key control. Kubernetes client certificate authentication does not allow for this due to a lack of support for certificate revocation.", @@ -1523,7 +1523,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.1 Authentication and Authorization", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes provides service account tokens which are intended for use by workloads running in the Kubernetes cluster, for authentication to the API server. These tokens are not designed for use by end-users and do not provide for features such as revocation or expiry, making them insecure. A newer version of the feature (Bound service account token volumes) does introduce expiry but still does not allow for specific revocation.", "RationaleStatement": "With any authentication mechanism the ability to revoke credentials if they are compromised or no longer required, is a key control. Service account token authentication does not allow for this due to the use of JWT tokens as an underlying technology.", @@ -1544,7 +1544,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.1 Authentication and Authorization", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes provides bootstrap tokens which are intended for use by new nodes joining the cluster These tokens are not designed for use by end-users they are specifically designed for the purpose of bootstrapping new nodes and not for general authentication", "RationaleStatement": "Bootstrap tokens are not intended for use as a general authentication mechanism and impose constraints on user and group naming that do not facilitate good RBAC design. They also cannot be used with MFA resulting in a weak authentication mechanism being available.", @@ -1565,7 +1565,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.2 Logging", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes can audit the details of requests made to the API server. The `--audit-policy-file` flag must be set for this logging to be enabled.", "RationaleStatement": "Logging is an important detective control for all systems, to detect potential unauthorised access.", @@ -1609,7 +1609,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet` service file has permissions of `600` or more restrictive.", "RationaleStatement": "The `kubelet` service file controls various parameters that set the behavior of the `kubelet` service in the worker node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1632,7 +1632,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet` service file ownership is set to `root:root`.", "RationaleStatement": "The `kubelet` service file controls various parameters that set the behavior of the `kubelet` service in the worker node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1653,7 +1653,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "If `kube-proxy` is running, and if it is using a file-based kubeconfig file, ensure that the proxy kubeconfig file has permissions of `600` or more restrictive.", "RationaleStatement": "The `kube-proxy` kubeconfig file controls various parameters of the `kube-proxy` service in the worker node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system. It is possible to run `kube-proxy` with the kubeconfig parameters configured as a Kubernetes ConfigMap instead of a file. In this case, there is no proxy kubeconfig file.", @@ -1674,7 +1674,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "If `kube-proxy` is running, ensure that the file ownership of its kubeconfig file is set to `root:root`.", "RationaleStatement": "The kubeconfig file for `kube-proxy` controls various parameters for the `kube-proxy` service in the worker node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1697,7 +1697,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet.conf` file has permissions of `600` or more restrictive.", "RationaleStatement": "The `kubelet.conf` file is the kubeconfig file for the node, and controls various parameters that set the behavior and identity of the worker node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1720,7 +1720,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that the `kubelet.conf` file ownership is set to `root:root`.", "RationaleStatement": "The `kubelet.conf` file is the kubeconfig file for the node, and controls various parameters that set the behavior and identity of the worker node. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1741,7 +1741,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the certificate authorities file has permissions of `600` or more restrictive.", "RationaleStatement": "The certificate authorities file controls the authorities used to validate API requests. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1762,7 +1762,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the certificate authorities file ownership is set to `root:root`.", "RationaleStatement": "The certificate authorities file controls the authorities used to validate API requests. You should set its file ownership to maintain the integrity of the file. The file should be owned by `root:root`.", @@ -1785,7 +1785,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that if the kubelet refers to a configuration file with the `--config` argument, that file has permissions of 600 or more restrictive.", "RationaleStatement": "The kubelet reads various parameters, including security settings, from a config file specified by the `--config` argument. If this file is specified you should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -1808,7 +1808,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.1 Worker Node Configuration Files", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Ensure that if the kubelet refers to a configuration file with the `--config` argument, that file is owned by root:root.", "RationaleStatement": "The kubelet reads various parameters, including security settings, from a config file specified by the `--config` argument. If this file is specified you should restrict its file permissions to maintain the integrity of the file. The file should be owned by root:root.", @@ -1831,7 +1831,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Disable anonymous requests to the Kubelet server.", "RationaleStatement": "When enabled, requests that are not rejected by other configured authentication methods are treated as anonymous requests. These requests are then served by the Kubelet server. You should rely on authentication to authorize access and disallow anonymous requests.", @@ -1854,7 +1854,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not allow all requests. Enable explicit authorization.", "RationaleStatement": "Kubelets, by default, allow all authenticated requests (even anonymous ones) without needing explicit authorization checks from the apiserver. You should restrict this behavior and only allow explicitly authorized requests.", @@ -1877,7 +1877,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable Kubelet authentication using certificates.", "RationaleStatement": "The connections from the apiserver to the kubelet are used for fetching logs for pods, attaching (through kubectl) to running pods, and using the kubelet’s port-forwarding functionality. These connections terminate at the kubelet’s HTTPS endpoint. By default, the apiserver does not verify the kubelet’s serving certificate, which makes the connection subject to man-in-the-middle attacks, and unsafe to run over untrusted and/or public networks. Enabling Kubelet certificate authentication ensures that the apiserver could authenticate the Kubelet before submitting any requests.", @@ -1900,7 +1900,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Disable the read-only port.", "RationaleStatement": "The Kubelet process provides a read-only API in addition to the main Kubelet API. Unauthenticated access is provided to this read-only API which could possibly retrieve potentially sensitive information about the cluster.", @@ -1923,7 +1923,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not disable timeouts on streaming connections.", "RationaleStatement": "Setting idle timeouts ensures that you are protected against Denial-of-Service attacks, inactive connections and running out of ephemeral ports. **Note:** By default, `--streaming-connection-idle-timeout` is set to 4 hours which might be too high for your environment. Setting this as appropriate would additionally ensure that such streaming connections are timed out after serving legitimate use cases.", @@ -1946,7 +1946,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Allow Kubelet to manage iptables.", "RationaleStatement": "Kubelets can automatically manage the required changes to iptables based on how you choose your networking options for the pods. It is recommended to let kubelets manage the changes to iptables. This ensures that the iptables configuration remains in sync with pods networking configuration. Manually configuring iptables with dynamic pod network configuration changes might hamper the communication between pods/containers and to the outside world. You might have iptables rules too restrictive or too open.", @@ -1967,7 +1967,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not override node hostnames.", "RationaleStatement": "Overriding hostnames could potentially break TLS setup between the kubelet and the apiserver. Additionally, with overridden hostnames, it becomes increasingly difficult to associate logs with a particular node and process them for security analytics. Hence, you should setup your kubelet nodes with resolvable FQDNs and avoid overriding the hostnames with IPs.", @@ -2013,7 +2013,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Setup TLS connection on the Kubelets.", "RationaleStatement": "The connections from the apiserver to the kubelet are used for fetching logs for pods, attaching (through kubectl) to running pods, and using the kubelet’s port-forwarding functionality. These connections terminate at the kubelet’s HTTPS endpoint. By default, the apiserver does not verify the kubelet’s serving certificate, which makes the connection subject to man-in-the-middle attacks, and unsafe to run over untrusted and/or public networks.", @@ -2036,7 +2036,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Enable kubelet client certificate rotation.", "RationaleStatement": "The `--rotate-certificates` setting causes the kubelet to rotate its client certificates by creating new CSRs as its existing credentials expire. This automated periodic rotation ensures that the there is no downtime due to expired certificates and thus addressing availability in the CIA security triad. **Note:** This recommendation only applies if you let kubelets get their certificates from the API server. In case your kubelet certificates come from an outside authority/tool (e.g. Vault) then you need to take care of rotation yourself. **Note:** This feature also require the `RotateKubeletClientCertificate` feature gate to be enabled (which is the default since Kubernetes v1.7)", @@ -2057,7 +2057,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Enable kubelet server certificate rotation.", "RationaleStatement": "`RotateKubeletServerCertificate` causes the kubelet to both request a serving certificate after bootstrapping its client credentials and rotate the certificate as its existing credentials expire. This automated periodic rotation ensures that the there are no downtimes due to expired certificates and thus addressing availability in the CIA security triad. Note: This recommendation only applies if you let kubelets get their certificates from the API server. In case your kubelet certificates come from an outside authority/tool (e.g. Vault) then you need to take care of rotation yourself.", @@ -2080,7 +2080,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Kubelet is configured to only use strong cryptographic ciphers.", "RationaleStatement": "TLS ciphers have had a number of known vulnerabilities and weaknesses, which can reduce the protection provided by them. By default Kubernetes supports a number of TLS ciphersuites including some that have security concerns, weakening the protection provided.", @@ -2101,7 +2101,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Ensure that the Kubelet sets limits on the number of PIDs that can be created by pods running on the node.", "RationaleStatement": "By default pods running in a cluster can consume any number of PIDs, potentially exhausting the resources available on the node. Setting an appropriate limit reduces the risk of a denial of service attack on cluster nodes.", @@ -2124,7 +2124,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The RBAC role `cluster-admin` provides wide-ranging powers over the environment and should be used only where and when needed.", "RationaleStatement": "Kubernetes provides a set of default roles where RBAC is used. Some of these roles such as `cluster-admin` provide wide-ranging privileges which should only be applied where absolutely necessary. Roles such as `cluster-admin` allow super-user access to perform any action on any resource. When used in a `ClusterRoleBinding`, it gives full control over every resource in the cluster and in all namespaces. When used in a `RoleBinding`, it gives full control over every resource in the rolebinding's namespace, including the namespace itself.", @@ -2147,7 +2147,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The Kubernetes API stores secrets, which may be service account tokens for the Kubernetes API or credentials used by workloads in the cluster. Access to these secrets should be restricted to the smallest possible group of users to reduce the risk of privilege escalation.", "RationaleStatement": "Inappropriate access to secrets stored within the Kubernetes cluster can allow for an attacker to gain additional access to the Kubernetes cluster or external resources whose credentials are stored as secrets.", @@ -2170,7 +2170,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Worker Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Kubernetes Roles and ClusterRoles provide access to resources based on sets of objects and actions that can be taken on those objects. It is possible to set either of these to be the wildcard \"*\" which matches all items. Use of wildcards is not optimal from a security perspective as it may allow for inadvertent access to be granted when new resources are added to the Kubernetes API either as CRDs or in later versions of the product.", "RationaleStatement": "The principle of least privilege recommends that users are provided only the access required for their role and nothing more. The use of wildcard rights grants is likely to provide excessive rights to the Kubernetes API.", @@ -2193,7 +2193,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The ability to create pods in a namespace can provide a number of opportunities for privilege escalation, such as assigning privileged service accounts to these pods or mounting hostPaths with access to sensitive data (unless Pod Security Policies are implemented to restrict this access) As such, access to create new pods should be restricted to the smallest possible group of users.", "RationaleStatement": "The ability to create pods in a cluster opens up possibilities for privilege escalation and should be restricted, where possible.", @@ -2214,7 +2214,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The `default` service account should not be used to ensure that rights granted to applications can be more easily audited and reviewed.", "RationaleStatement": "Kubernetes provides a `default` service account which is used by cluster workloads where no specific service account is assigned to the pod. Where access to the Kubernetes API from a pod is required, a specific service account should be created for that pod, and rights granted to that service account. The default service account should be configured such that it does not provide a service account token and does not have any explicit rights assignments.", @@ -2235,7 +2235,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Service accounts tokens should not be mounted in pods except where the workload running in the pod explicitly needs to communicate with the API server", "RationaleStatement": "Mounting service account tokens inside pods can provide an avenue for privilege escalation attacks where an attacker is able to compromise a single pod in the cluster. Avoiding mounting these tokens removes this attack avenue.", @@ -2256,7 +2256,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The special group `system:masters` should not be used to grant permissions to any user or service account, except where strictly necessary (e.g. bootstrapping access prior to RBAC being fully available)", "RationaleStatement": "The `system:masters` group has unrestricted access to the Kubernetes API hard-coded into the API server source code. An authenticated user who is a member of this group cannot have their access reduced, even if all bindings and cluster role bindings which mention it, are removed. When combined with client certificate authentication, use of this group can allow for irrevocable cluster-admin level credentials to exist for a cluster.", @@ -2277,7 +2277,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Cluster roles and roles with the impersonate, bind or escalate permissions should not be granted unless strictly required. Each of these permissions allow a particular subject to escalate their privileges beyond those explicitly granted by cluster administrators", "RationaleStatement": "The impersonate privilege allows a subject to impersonate other users gaining their rights to the cluster. The bind privilege allows the subject to add a binding to a cluster role or role which escalates their effective permissions in the cluster. The escalate privilege allows a subject to modify cluster roles to which they are bound, increasing their rights to that level. Each of these permissions has the potential to allow for privilege escalation to cluster-admin level.", @@ -2300,7 +2300,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "The ability to create persistent volumes in a cluster can provide an opportunity for privilege escalation, via the creation of `hostPath` volumes. As persistent volumes are not covered by Pod Security Admission, a user with access to create persistent volumes may be able to get access to sensitive files from the underlying host even where restrictive Pod Security Admission policies are in place.", "RationaleStatement": "The ability to create persistent volumes in a cluster opens up possibilities for privilege escalation and should be restricted, where possible.", @@ -2323,7 +2323,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with access to the `Proxy` sub-resource of `Node` objects automatically have permissions to use the Kubelet API, which may allow for privilege escalation or bypass cluster security controls such as audit logs. The Kubelet provides an API which includes rights to execute commands in any container running on the node. Access to this API is covered by permissions to the main Kubernetes API via the `node` object. The proxy sub-resource specifically allows wide ranging access to the Kubelet API. Direct access to the Kubelet API bypasses controls like audit logging (there is no audit log of Kubelet API access) and admission control.", "RationaleStatement": "The ability to use the `proxy` sub-resource of `node` objects opens up possibilities for privilege escalation and should be restricted, where possible.", @@ -2346,7 +2346,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with access to the update the `approval` sub-resource of `certificateaigningrequest` objects can approve new client certificates for the Kubernetes API effectively allowing them to create new high-privileged user accounts. This can allow for privilege escalation to full cluster administrator, depending on users configured in the cluster", "RationaleStatement": "The ability to update certificate signing requests should be limited.", @@ -2369,7 +2369,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with rights to create/modify/delete `validatingwebhookconfigurations` or `mutatingwebhookconfigurations` can control webhooks that can read any object admitted to the cluster, and in the case of mutating webhooks, also mutate admitted objects. This could allow for privilege escalation or disruption of the operation of the cluster.", "RationaleStatement": "The ability to manage webhook configuration should be limited", @@ -2392,7 +2392,7 @@ { "Section": "5 Policies", "SubSection": "5.1 RBAC and Service Accounts", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Users with rights to create new service account tokens at a cluster level, can create long-lived privileged credentials in the cluster. This could allow for privilege escalation and persistent access to the cluster, even if the users account has been revoked.", "RationaleStatement": "The ability to create service account tokens should be limited.", @@ -2413,7 +2413,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Every Kubernetes cluster should have at least one policy control mechanism in place to enforce the other requirements in this section. This could be the in-built Pod Security Admission controller, or a third party policy control system.", "RationaleStatement": "Without an active policy control mechanism, it is not possible to limit the use of containers with access to underlying cluster nodes, via mechanisms like privileged containers, or the use of hostPath volume mounts.", @@ -2436,7 +2436,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run with the `securityContext.privileged` flag set to `true`.", "RationaleStatement": "Privileged containers have access to all Linux Kernel capabilities and devices. A container running with full privileges can do almost everything that the host can do. This flag exists to allow special use-cases, like manipulating the network stack and accessing devices. There should be at least one admission control policy defined which does not permit privileged containers. If you need to run privileged containers, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2459,7 +2459,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not generally permit containers to be run with the `hostPID` flag set to true.", "RationaleStatement": "A container running in the host's PID namespace can inspect processes running outside the container. If the container also has access to ptrace capabilities this can be used to escalate privileges outside of the container. There should be at least one admission control policy defined which does not permit containers to share the host PID namespace. If you need to run containers which require hostPID, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2482,7 +2482,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not generally permit containers to be run with the `hostIPC` flag set to true.", "RationaleStatement": "A container running in the host's IPC namespace can use IPC to interact with processes outside the container. There should be at least one admission control policy defined which does not permit containers to share the host IPC namespace. If you need to run containers which require hostIPC, this should be definited in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2505,7 +2505,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not generally permit containers to be run with the `hostNetwork` flag set to true.", "RationaleStatement": "A container running in the host's network namespace could access the local loopback device, and could access network traffic to and from other pods. There should be at least one admission control policy defined which does not permit containers to share the host network namespace. If you need to run containers which require access to the host's network namesapces, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2528,7 +2528,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not generally permit containers to be run with the `allowPrivilegeEscalation` flag set to true. Allowing this right can lead to a process running a container getting more rights than it started with. It's important to note that these rights are still constrained by the overall container sandbox, and this setting does not relate to the use of privileged containers.", "RationaleStatement": "A container running with the `allowPrivilegeEscalation` flag set to `true` may have processes that can gain more privileges than their parent. There should be at least one admission control policy defined which does not permit containers to allow privilege escalation. The option exists (and is defaulted to true) to permit setuid binaries to run. If you have need to run containers which use setuid binaries or require privilege escalation, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2574,7 +2574,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not generally permit containers with the potentially dangerous NET_RAW capability.", "RationaleStatement": "Containers run with a default set of capabilities as assigned by the Container Runtime. By default this can include potentially dangerous capabilities. With Docker as the container runtime the NET_RAW capability is enabled which may be misused by malicious containers. Ideally, all containers should drop this capability. There should be at least one admission control policy defined which does not permit containers with the NET_RAW capability. If you need to run containers with this capability, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2597,7 +2597,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Automated", "Description": "Do not generally permit containers with capabilities assigned beyond the default set.", "RationaleStatement": "Containers run with a default set of capabilities as assigned by the Container Runtime. Capabilities outside this set can be added to containers which could expose them to risks of container breakout attacks. There should be at least one policy defined which prevents containers with capabilities beyond the default set from launching. If you need to run containers with additional capabilities, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2643,7 +2643,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit Windows containers to be run with the `hostProcess` flag set to true.", "RationaleStatement": "A Windows container making use of the `hostProcess` flag can interact with the underlying Windows cluster node. As per the Kubernetes documentation, this provides \"privileged access\" to the Windows node. Where Windows containers are used inside a Kubernetes cluster, there should be at least one admission control policy which does not permit `hostProcess` Windows containers. If you need to run Windows containers which require `hostProcess`, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2664,7 +2664,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally admit containers which make use of `hostPath` volumes.", "RationaleStatement": "A container which mounts a `hostPath` volume as part of its specification will have access to the filesystem of the underlying cluster node. The use of `hostPath` volumes may allow containers access to privileged areas of the node filesystem. There should be at least one admission control policy defined which does not permit containers to mount `hostPath` volumes. If you need to run containers which require `hostPath` volumes, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2687,7 +2687,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers which require the use of HostPorts.", "RationaleStatement": "Host ports connect containers directly to the host's network. This can bypass controls such as network policy. There should be at least one admission control policy defined which does not permit containers which require the use of HostPorts. If you need to run containers which require HostPorts, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2708,7 +2708,7 @@ { "Section": "5 Policies", "SubSection": "5.3 Network Policies and CNI", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "There are a variety of CNI plugins available for Kubernetes. If the CNI in use does not support Network Policies it may not be possible to effectively restrict traffic in the cluster.", "RationaleStatement": "Kubernetes network policies are enforced by the CNI plugin in use. As such it is important to ensure that the CNI plugin supports both Ingress and Egress network policies.", @@ -2815,7 +2815,7 @@ { "Section": "5 Policies", "SubSection": "5.7 General Policies", - "Profile": "Level 1 - Master Node", + "Profile": "Level 1", "AssessmentStatus": "Manual", "Description": "Use namespaces to isolate your Kubernetes objects.", "RationaleStatement": "Limiting the scope of user permissions can reduce the impact of mistakes or malicious activities. A Kubernetes namespace allows you to partition created resources into logically named groups. Resources created in one namespace can be hidden from other namespaces. By default, each resource created by a user in Kubernetes cluster runs in a default namespace, called `default`. You can create additional namespaces and attach resources and users to them. You can use Kubernetes Authorization plugins to create policies that segregate access to namespace resources between different users.", diff --git a/prowler/lib/check/compliance_models.py b/prowler/lib/check/compliance_models.py index 428bd85ad1..8ed8ab45bb 100644 --- a/prowler/lib/check/compliance_models.py +++ b/prowler/lib/check/compliance_models.py @@ -56,22 +56,22 @@ class ENS_Requirement_Attribute(BaseModel): class Generic_Compliance_Requirement_Attribute(BaseModel): """Generic Compliance Requirement Attribute""" - ItemId: Optional[str] - Section: Optional[str] - SubSection: Optional[str] - SubGroup: Optional[str] - Service: Optional[str] - Type: Optional[str] + ItemId: Optional[str] = None + Section: Optional[str] = None + SubSection: Optional[str] = None + SubGroup: Optional[str] = None + Service: Optional[str] = None + Type: Optional[str] = None -class CIS_Requirement_Attribute_Profile(str): +class CIS_Requirement_Attribute_Profile(str, Enum): """CIS Requirement Attribute Profile""" Level_1 = "Level 1" Level_2 = "Level 2" -class CIS_Requirement_Attribute_AssessmentStatus(str): +class CIS_Requirement_Attribute_AssessmentStatus(str, Enum): """CIS Requirement Attribute Assessment Status""" Manual = "Manual" @@ -80,11 +80,10 @@ class CIS_Requirement_Attribute_AssessmentStatus(str): # CIS Requirement Attribute class CIS_Requirement_Attribute(BaseModel): - model_config = {"arbitrary_types_allowed": True} """CIS Requirement Attribute""" Section: str - SubSection: Optional[str] + SubSection: Optional[str] = None Profile: CIS_Requirement_Attribute_Profile AssessmentStatus: CIS_Requirement_Attribute_AssessmentStatus Description: str @@ -93,7 +92,7 @@ class CIS_Requirement_Attribute(BaseModel): RemediationProcedure: str AuditProcedure: str AdditionalInformation: str - DefaultValue: Optional[str] + DefaultValue: Optional[str] = None References: str @@ -105,7 +104,7 @@ class AWS_Well_Architected_Requirement_Attribute(BaseModel): WellArchitectedQuestionId: str WellArchitectedPracticeId: str Section: str - SubSection: Optional[str] + SubSection: Optional[str] = None LevelOfRisk: str AssessmentMethod: str Description: str @@ -178,10 +177,10 @@ class KISA_ISMSP_Requirement_Attribute(BaseModel): Domain: str Subdomain: str Section: str - AuditChecklist: Optional[list[str]] - RelatedRegulations: Optional[list[str]] - AuditEvidence: Optional[list[str]] - NonComplianceCases: Optional[list[str]] + AuditChecklist: Optional[list[str]] = None + RelatedRegulations: Optional[list[str]] = None + AuditEvidence: Optional[list[str]] = None + NonComplianceCases: Optional[list[str]] = None # Prowler ThreatScore Requirement Attribute @@ -204,7 +203,7 @@ class Compliance_Requirement(BaseModel): Id: str Description: str - Name: Optional[str] + Name: Optional[str] = None Attributes: list[ Union[ CIS_Requirement_Attribute, @@ -225,7 +224,7 @@ class Compliance(BaseModel): Framework: str Provider: str - Version: Optional[str] + Version: Optional[str] = None Description: str Requirements: list[ Union[ diff --git a/prowler/lib/check/models.py b/prowler/lib/check/models.py index 31807947d1..d0dab6ca59 100644 --- a/prowler/lib/check/models.py +++ b/prowler/lib/check/models.py @@ -5,7 +5,7 @@ import sys from abc import ABC, abstractmethod from dataclasses import asdict, dataclass, is_dataclass from enum import Enum -from typing import Any, Dict, Set +from typing import Any, Dict, Optional, Set from pydantic import BaseModel, ValidationError, validator @@ -118,7 +118,7 @@ class CheckMetadata(BaseModel): Notes: str # We set the compliance to None to # store the compliance later if supplied - Compliance: list = None + Compliance: Optional[list[Any]] = [] @validator("Categories", each_item=True, pre=True, always=True) def valid_category(value): diff --git a/prowler/lib/outputs/finding.py b/prowler/lib/outputs/finding.py index 428637e4f3..2df79a954a 100644 --- a/prowler/lib/outputs/finding.py +++ b/prowler/lib/outputs/finding.py @@ -38,7 +38,7 @@ class Finding(BaseModel): account_organization_uid: Optional[str] = None account_organization_name: Optional[str] = None metadata: CheckMetadata - account_tags: dict = {} + account_tags: dict = Field(default_factory=dict) uid: str status: Status status_extended: str @@ -50,7 +50,7 @@ class Finding(BaseModel): resource_tags: dict = Field(default_factory=dict) partition: Optional[str] = None region: str - compliance: dict + compliance: dict = Field(default_factory=dict) prowler_version: str = prowler_version raw: dict = Field(default_factory=dict) diff --git a/prowler/providers/common/models.py b/prowler/providers/common/models.py index bac7f29943..47c9ebd2fe 100644 --- a/prowler/providers/common/models.py +++ b/prowler/providers/common/models.py @@ -9,10 +9,6 @@ from prowler.providers.common.provider import Provider # TODO: include this for all the providers class Audit_Metadata(BaseModel): - provider: str - account_id: str - account_name: str - region: str services_scanned: int # We can't use a set in the expected # checks because the set is unordered diff --git a/prowler/providers/gcp/models.py b/prowler/providers/gcp/models.py index 3505293fd8..4abdd6b0ff 100644 --- a/prowler/providers/gcp/models.py +++ b/prowler/providers/gcp/models.py @@ -21,7 +21,7 @@ class GCPProject(BaseModel): number: str id: str name: str - organization: Optional[GCPOrganization] + organization: Optional[GCPOrganization] = None labels: dict lifecycle_state: str diff --git a/prowler/providers/iac/iac_provider.py b/prowler/providers/iac/iac_provider.py index f69e4a0a63..5b44ba06a1 100644 --- a/prowler/providers/iac/iac_provider.py +++ b/prowler/providers/iac/iac_provider.py @@ -90,16 +90,22 @@ class IACProvider(Provider): try: output = json.loads(process.stdout) + if not output: + logger.warning("No findings returned from Checkov scan") + return [] except json.JSONDecodeError: logger.error("Failed to parse IaC scan output as JSON") return [] reports = [] - # TODO: Process passed checks # Process failed checks for finding in output: - for failed_check in finding.get("results", {}).get("failed_checks", []): + failed_checks = finding.get("results", {}).get("failed_checks", []) + if not failed_checks: + continue + + for failed_check in failed_checks: # Get severity and ensure it's a valid string severity = finding.get("severity", "low") if severity is None: @@ -144,6 +150,7 @@ class IACProvider(Provider): "DependsOn": [], "RelatedTo": [], "Notes": "", + "GCPProject": failed_check.get("project_id", ""), } # Convert metadata dict to JSON string diff --git a/tests/lib/cli/parser_test.py b/tests/lib/cli/parser_test.py index 1c72443baf..09b11f5637 100644 --- a/tests/lib/cli/parser_test.py +++ b/tests/lib/cli/parser_test.py @@ -17,9 +17,7 @@ prowler_command = "prowler" # capsys # https://docs.pytest.org/en/7.1.x/how-to/capture-stdout-stderr.html -prowler_default_usage_error = ( - "usage: prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,nhn,dashboard} ..." -) +prowler_default_usage_error = "usage: prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,nhn,dashboard,iac} ..." def mock_get_available_providers(): diff --git a/tests/lib/outputs/compliance/cis/cis_kubernetes_test.py b/tests/lib/outputs/compliance/cis/cis_kubernetes_test.py index b6e4a4d4f9..7f8b6ccbd0 100644 --- a/tests/lib/outputs/compliance/cis/cis_kubernetes_test.py +++ b/tests/lib/outputs/compliance/cis/cis_kubernetes_test.py @@ -181,5 +181,5 @@ class TestKubernetesCIS: mock_file.seek(0) content = mock_file.read() - expected_csv = f"PROVIDER;DESCRIPTION;CONTEXT;NAMESPACE;ASSESSMENTDATE;REQUIREMENTS_ID;REQUIREMENTS_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_SECTION;REQUIREMENTS_ATTRIBUTES_SUBSECTION;REQUIREMENTS_ATTRIBUTES_PROFILE;REQUIREMENTS_ATTRIBUTES_ASSESSMENTSTATUS;REQUIREMENTS_ATTRIBUTES_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_RATIONALESTATEMENT;REQUIREMENTS_ATTRIBUTES_IMPACTSTATEMENT;REQUIREMENTS_ATTRIBUTES_REMEDIATIONPROCEDURE;REQUIREMENTS_ATTRIBUTES_AUDITPROCEDURE;REQUIREMENTS_ATTRIBUTES_ADDITIONALINFORMATION;REQUIREMENTS_ATTRIBUTES_REFERENCES;REQUIREMENTS_ATTRIBUTES_DEFAULTVALUE;STATUS;STATUSEXTENDED;RESOURCEID;RESOURCENAME;CHECKID;MUTED\r\nkubernetes;This CIS Kubernetes Benchmark provides prescriptive guidance for establishing a secure configuration posture for Kubernetes v1.27.;test-cluster;test-namespace;{datetime.now()};1.1.3;Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive;1. Control Plane;1.1 Control Plane Node Configuration Files;Level 1 - Master Node;Automated;Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.;The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.;;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` chmod 600 /etc/kubernetes/manifests/kube-controller-manager.yaml ```;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml ``` Verify that the permissions are `600` or more restrictive.;;https://kubernetes.io/docs/admin/kube-apiserver/;By default, the `kube-controller-manager.yaml` file has permissions of `640`.;PASS;;;;test-check-id;False\r\nkubernetes;This CIS Kubernetes Benchmark provides prescriptive guidance for establishing a secure configuration posture for Kubernetes v1.27.;;;{datetime.now()};1.1.4;Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive;1.1 Control Plane Node Configuration Files;;Level 1 - Master Node;Automated;Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.;The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.;;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` chmod 600 /etc/kubernetes/manifests/kube-controller-manager.yaml ```;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml ``` Verify that the permissions are `600` or more restrictive.;;https://kubernetes.io/docs/admin/kube-apiserver/;By default, the `kube-controller-manager.yaml` file has permissions of `640`.;MANUAL;Manual check;manual_check;Manual check;manual;False\r\n" + expected_csv = f"PROVIDER;DESCRIPTION;CONTEXT;NAMESPACE;ASSESSMENTDATE;REQUIREMENTS_ID;REQUIREMENTS_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_SECTION;REQUIREMENTS_ATTRIBUTES_SUBSECTION;REQUIREMENTS_ATTRIBUTES_PROFILE;REQUIREMENTS_ATTRIBUTES_ASSESSMENTSTATUS;REQUIREMENTS_ATTRIBUTES_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_RATIONALESTATEMENT;REQUIREMENTS_ATTRIBUTES_IMPACTSTATEMENT;REQUIREMENTS_ATTRIBUTES_REMEDIATIONPROCEDURE;REQUIREMENTS_ATTRIBUTES_AUDITPROCEDURE;REQUIREMENTS_ATTRIBUTES_ADDITIONALINFORMATION;REQUIREMENTS_ATTRIBUTES_REFERENCES;REQUIREMENTS_ATTRIBUTES_DEFAULTVALUE;STATUS;STATUSEXTENDED;RESOURCEID;RESOURCENAME;CHECKID;MUTED\r\nkubernetes;This CIS Kubernetes Benchmark provides prescriptive guidance for establishing a secure configuration posture for Kubernetes v1.27.;test-cluster;test-namespace;{datetime.now()};1.1.3;Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive;1. Control Plane;1.1 Control Plane Node Configuration Files;Level 1;Automated;Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.;The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.;;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` chmod 600 /etc/kubernetes/manifests/kube-controller-manager.yaml ```;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml ``` Verify that the permissions are `600` or more restrictive.;;https://kubernetes.io/docs/admin/kube-apiserver/;By default, the `kube-controller-manager.yaml` file has permissions of `640`.;PASS;;;;test-check-id;False\r\nkubernetes;This CIS Kubernetes Benchmark provides prescriptive guidance for establishing a secure configuration posture for Kubernetes v1.27.;;;{datetime.now()};1.1.4;Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive;1.1 Control Plane Node Configuration Files;;Level 1;Automated;Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.;The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.;;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` chmod 600 /etc/kubernetes/manifests/kube-controller-manager.yaml ```;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml ``` Verify that the permissions are `600` or more restrictive.;;https://kubernetes.io/docs/admin/kube-apiserver/;By default, the `kube-controller-manager.yaml` file has permissions of `640`.;MANUAL;Manual check;manual_check;Manual check;manual;False\r\n" assert content == expected_csv diff --git a/tests/lib/outputs/compliance/cis/cis_m365_test.py b/tests/lib/outputs/compliance/cis/cis_m365_test.py index 1c8232d73a..ebfe4a3215 100644 --- a/tests/lib/outputs/compliance/cis/cis_m365_test.py +++ b/tests/lib/outputs/compliance/cis/cis_m365_test.py @@ -177,6 +177,6 @@ class TestM365CIS: mock_file.seek(0) content = mock_file.read() - expected_csv = f"PROVIDER;DESCRIPTION;TENANTID;LOCATION;ASSESSMENTDATE;REQUIREMENTS_ID;REQUIREMENTS_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_SECTION;REQUIREMENTS_ATTRIBUTES_SUBSECTION;REQUIREMENTS_ATTRIBUTES_PROFILE;REQUIREMENTS_ATTRIBUTES_ASSESSMENTSTATUS;REQUIREMENTS_ATTRIBUTES_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_RATIONALESTATEMENT;REQUIREMENTS_ATTRIBUTES_IMPACTSTATEMENT;REQUIREMENTS_ATTRIBUTES_REMEDIATIONPROCEDURE;REQUIREMENTS_ATTRIBUTES_AUDITPROCEDURE;REQUIREMENTS_ATTRIBUTES_ADDITIONALINFORMATION;REQUIREMENTS_ATTRIBUTES_DEFAULTVALUE;REQUIREMENTS_ATTRIBUTES_REFERENCES;STATUS;STATUSEXTENDED;RESOURCEID;RESOURCENAME;CHECKID;MUTED\r\nm365;The CIS Microsoft 365 Foundations Benchmark provides prescriptive guidance for configuring security options for Microsoft 365 with an emphasis on foundational, testable, and architecture agnostic settings.;00000000-0000-0000-0000-000000000000;global;{datetime.now()};2.1.3;Ensure MFA Delete is enabled on S3 buckets;2.1. Simple Storage Service (S3);;Level 1;Automated;Once MFA Delete is enabled on your sensitive and classified S3 bucket it requires the user to have two forms of authentication.;Adding MFA delete to an S3 bucket, requires additional authentication when you change the version state of your bucket or you delete and object version adding another layer of security in the event your security credentials are compromised or unauthorized access is granted.;;Perform the steps below to enable MFA delete on an S3 bucket.Note:-You cannot enable MFA Delete using the AWS Management Console. You must use the AWS CLI or API.-You must use your 'root' account to enable MFA Delete on S3 buckets.**From Command line:**1. Run the s3api put-bucket-versioning command aws s3api put-bucket-versioning --profile my-root-profile --bucket Bucket_Name --versioning-configuration Status=Enabled,MFADelete=Enabled --mfa arn:aws:iam::aws_account_id:mfa/root-account-mfa-device passcode;Perform the steps below to confirm MFA delete is configured on an S3 Bucket**From Console:**1. Login to the S3 console at `https://console.aws.amazon.com/s3/`2. Click the `Check` box next to the Bucket name you want to confirm3. In the window under `Properties`4. Confirm that Versioning is `Enabled`5. Confirm that MFA Delete is `Enabled`**From Command Line:**1. Run the `get-bucket-versioning aws s3api get-bucket-versioning --bucket my-bucket Output example: Enabled Enabled\ If the Console or the CLI output does not show Versioning and MFA Delete `enabled` refer to the remediation below.;;By default, MFA Delete is not enabled on S3 buckets.;https://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html#MultiFactorAuthenticationDelete:https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMFADelete.html:https://aws.amazon.com/blogs/security/securing-access-to-aws-using-mfa-part-3/:https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_lost-or-broken.html;PASS;;;;test-check-id;False\r\nm365;The CIS Microsoft 365 Foundations Benchmark provides prescriptive guidance for configuring security options for Microsoft 365 with an emphasis on foundational, testable, and architecture agnostic settings.;00000000-0000-0000-0000-000000000000;global;{datetime.now()};2.1.4;Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive;1.1 Control Plane Node Configuration Files;;Level 1 - Master Node;Automated;Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.;The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.;;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` chmod 600 /etc/kubernetes/manifests/kube-controller-manager.yaml ```;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml ``` Verify that the permissions are `600` or more restrictive.;;By default, the `kube-controller-manager.yaml` file has permissions of `640`.;https://kubernetes.io/docs/admin/kube-apiserver/;MANUAL;Manual check;manual_check;Manual check;manual;False\r\n" + expected_csv = f"PROVIDER;DESCRIPTION;TENANTID;LOCATION;ASSESSMENTDATE;REQUIREMENTS_ID;REQUIREMENTS_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_SECTION;REQUIREMENTS_ATTRIBUTES_SUBSECTION;REQUIREMENTS_ATTRIBUTES_PROFILE;REQUIREMENTS_ATTRIBUTES_ASSESSMENTSTATUS;REQUIREMENTS_ATTRIBUTES_DESCRIPTION;REQUIREMENTS_ATTRIBUTES_RATIONALESTATEMENT;REQUIREMENTS_ATTRIBUTES_IMPACTSTATEMENT;REQUIREMENTS_ATTRIBUTES_REMEDIATIONPROCEDURE;REQUIREMENTS_ATTRIBUTES_AUDITPROCEDURE;REQUIREMENTS_ATTRIBUTES_ADDITIONALINFORMATION;REQUIREMENTS_ATTRIBUTES_DEFAULTVALUE;REQUIREMENTS_ATTRIBUTES_REFERENCES;STATUS;STATUSEXTENDED;RESOURCEID;RESOURCENAME;CHECKID;MUTED\r\nm365;The CIS Microsoft 365 Foundations Benchmark provides prescriptive guidance for configuring security options for Microsoft 365 with an emphasis on foundational, testable, and architecture agnostic settings.;00000000-0000-0000-0000-000000000000;global;{datetime.now()};2.1.3;Ensure MFA Delete is enabled on S3 buckets;2.1. Simple Storage Service (S3);;Level 1;Automated;Once MFA Delete is enabled on your sensitive and classified S3 bucket it requires the user to have two forms of authentication.;Adding MFA delete to an S3 bucket, requires additional authentication when you change the version state of your bucket or you delete and object version adding another layer of security in the event your security credentials are compromised or unauthorized access is granted.;;Perform the steps below to enable MFA delete on an S3 bucket.Note:-You cannot enable MFA Delete using the AWS Management Console. You must use the AWS CLI or API.-You must use your 'root' account to enable MFA Delete on S3 buckets.**From Command line:**1. Run the s3api put-bucket-versioning command aws s3api put-bucket-versioning --profile my-root-profile --bucket Bucket_Name --versioning-configuration Status=Enabled,MFADelete=Enabled --mfa arn:aws:iam::aws_account_id:mfa/root-account-mfa-device passcode;Perform the steps below to confirm MFA delete is configured on an S3 Bucket**From Console:**1. Login to the S3 console at `https://console.aws.amazon.com/s3/`2. Click the `Check` box next to the Bucket name you want to confirm3. In the window under `Properties`4. Confirm that Versioning is `Enabled`5. Confirm that MFA Delete is `Enabled`**From Command Line:**1. Run the `get-bucket-versioning aws s3api get-bucket-versioning --bucket my-bucket Output example: Enabled Enabled\ If the Console or the CLI output does not show Versioning and MFA Delete `enabled` refer to the remediation below.;;By default, MFA Delete is not enabled on S3 buckets.;https://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html#MultiFactorAuthenticationDelete:https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMFADelete.html:https://aws.amazon.com/blogs/security/securing-access-to-aws-using-mfa-part-3/:https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_lost-or-broken.html;PASS;;;;test-check-id;False\r\nm365;The CIS Microsoft 365 Foundations Benchmark provides prescriptive guidance for configuring security options for Microsoft 365 with an emphasis on foundational, testable, and architecture agnostic settings.;00000000-0000-0000-0000-000000000000;global;{datetime.now()};2.1.4;Ensure that the controller manager pod specification file permissions are set to 600 or more restrictive;1.1 Control Plane Node Configuration Files;;Level 1;Automated;Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.;The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.;;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` chmod 600 /etc/kubernetes/manifests/kube-controller-manager.yaml ```;Run the below command (based on the file location on your system) on the Control Plane node. For example, ``` stat -c %a /etc/kubernetes/manifests/kube-controller-manager.yaml ``` Verify that the permissions are `600` or more restrictive.;;By default, the `kube-controller-manager.yaml` file has permissions of `640`.;https://kubernetes.io/docs/admin/kube-apiserver/;MANUAL;Manual check;manual_check;Manual check;manual;False\r\n" assert content == expected_csv diff --git a/tests/lib/outputs/compliance/fixtures.py b/tests/lib/outputs/compliance/fixtures.py index ddf25ad661..427c467776 100644 --- a/tests/lib/outputs/compliance/fixtures.py +++ b/tests/lib/outputs/compliance/fixtures.py @@ -179,7 +179,7 @@ CIS_1_8_KUBERNETES = Compliance( CIS_Requirement_Attribute( Section="1. Control Plane", SubSection="1.1 Control Plane Node Configuration Files", - Profile="Level 1 - Master Node", + Profile="Level 1", AssessmentStatus="Automated", Description="Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.", RationaleStatement="The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -199,7 +199,7 @@ CIS_1_8_KUBERNETES = Compliance( Attributes=[ CIS_Requirement_Attribute( Section="1.1 Control Plane Node Configuration Files", - Profile="Level 1 - Master Node", + Profile="Level 1", AssessmentStatus="Automated", Description="Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.", RationaleStatement="The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", @@ -279,7 +279,7 @@ CIS_4_0_M365 = Compliance( Attributes=[ CIS_Requirement_Attribute( Section="1.1 Control Plane Node Configuration Files", - Profile="Level 1 - Master Node", + Profile="Level 1", AssessmentStatus="Automated", Description="Ensure that the controller manager pod specification file has permissions of `600` or more restrictive.", RationaleStatement="The controller manager pod specification file controls various parameters that set the behavior of the Controller Manager on the master node. You should restrict its file permissions to maintain the integrity of the file. The file should be writable by only the administrators on the system.", diff --git a/tests/lib/outputs/finding_test.py b/tests/lib/outputs/finding_test.py index 3dea2f614b..d849a8760a 100644 --- a/tests/lib/outputs/finding_test.py +++ b/tests/lib/outputs/finding_test.py @@ -573,6 +573,16 @@ class TestFinding: inserted_at = 1234567890 provider = DummyProvider(uid="account123") provider.type = "aws" + provider.organizations_metadata = SimpleNamespace( + account_name="test-account", + account_email="test@example.com", + organization_arn="arn:aws:organizations::123456789012:organization/o-abcdef123456", + organization_id="o-abcdef123456", + account_tags={"Environment": "prod", "Project": "test"}, + ) + provider.identity = SimpleNamespace( + account="123456789012", partition="aws", profile="default" + ) scan = DummyScan(provider=provider) # Create a dummy resource with one tag @@ -655,7 +665,10 @@ class TestFinding: assert meta.Notes == "Some notes" # Check other Finding fields - assert finding_obj.uid == "prowler-aws-check-001--us-east-1-ResourceName1" + assert ( + finding_obj.uid + == "prowler-aws-check-001-123456789012-us-east-1-ResourceName1" + ) assert finding_obj.status == Status("FAIL") assert finding_obj.status_extended == "extended" # From the dummy resource @@ -665,6 +678,7 @@ class TestFinding: # unroll_tags is called on a list with one tag -> expect {"env": "prod"} assert finding_obj.resource_tags == {"env": "prod"} assert finding_obj.region == "us-east-1" + assert finding_obj.account_tags == {"Environment": "prod", "Project": "test"} assert finding_obj.compliance == { "CIS-2.0": ["1.12"], "CIS-3.0": ["1.12"], From 4ed803331a1c4b05c0a5308409d5ac5da6b31e78 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Tue, 27 May 2025 17:41:51 +0200 Subject: [PATCH 05/24] fix: tests --- tests/lib/outputs/finding_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/lib/outputs/finding_test.py b/tests/lib/outputs/finding_test.py index d849a8760a..4d3fe43156 100644 --- a/tests/lib/outputs/finding_test.py +++ b/tests/lib/outputs/finding_test.py @@ -678,7 +678,6 @@ class TestFinding: # unroll_tags is called on a list with one tag -> expect {"env": "prod"} assert finding_obj.resource_tags == {"env": "prod"} assert finding_obj.region == "us-east-1" - assert finding_obj.account_tags == {"Environment": "prod", "Project": "test"} assert finding_obj.compliance == { "CIS-2.0": ["1.12"], "CIS-3.0": ["1.12"], From a71ec4711ba22d1756863bc2b41b48b9920c5f58 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Tue, 27 May 2025 17:43:27 +0200 Subject: [PATCH 06/24] chore: revision --- prowler/providers/common/provider.py | 9 ++------- prowler/providers/iac/iac.py | 3 --- prowler/providers/iac/iac_provider.py | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) delete mode 100644 prowler/providers/iac/iac.py diff --git a/prowler/providers/common/provider.py b/prowler/providers/common/provider.py index 4d950e49d6..44e7b067c3 100644 --- a/prowler/providers/common/provider.py +++ b/prowler/providers/common/provider.py @@ -149,11 +149,7 @@ class Provider(ABC): provider_class_path = ( f"{providers_path}.{arguments.provider}.{arguments.provider}_provider" ) - # Special case for IAC provider - if arguments.provider == "iac": - provider_class_name = "IACProvider" - else: - provider_class_name = f"{arguments.provider.capitalize()}Provider" + provider_class_name = f"{arguments.provider.capitalize()}Provider" provider_class = getattr( import_module(provider_class_path), provider_class_name ) @@ -248,13 +244,12 @@ class Provider(ABC): config_path=arguments.config_file, ) elif "iac" in provider_class_name.lower(): - provider = provider_class( + provider_class( scan_path=arguments.scan_path, config_path=arguments.config_file, mutelist_path=arguments.mutelist_file, fixer_config=fixer_config, ) - Provider.set_global_provider(provider) except TypeError as error: logger.critical( diff --git a/prowler/providers/iac/iac.py b/prowler/providers/iac/iac.py deleted file mode 100644 index 4307360a9f..0000000000 --- a/prowler/providers/iac/iac.py +++ /dev/null @@ -1,3 +0,0 @@ -from prowler.providers.iac.iac_provider import IACProvider - -provider = IACProvider(scan_path=globals().get("args", None).scan_path) diff --git a/prowler/providers/iac/iac_provider.py b/prowler/providers/iac/iac_provider.py index 5b44ba06a1..a2e3e502ec 100644 --- a/prowler/providers/iac/iac_provider.py +++ b/prowler/providers/iac/iac_provider.py @@ -12,7 +12,7 @@ from prowler.providers.common.models import Audit_Metadata from prowler.providers.common.provider import Provider -class IACProvider(Provider): +class IacProvider(Provider): _type: str = "iac" audit_metadata: Audit_Metadata From d6566f2da7416abe903c2525ad6f1445500e5a42 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Tue, 27 May 2025 17:51:57 +0200 Subject: [PATCH 07/24] fix: poetry lock --- poetry.lock | 2 +- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 55aa81a198..241c8a8232 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6475,4 +6475,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">3.9.1,<3.13" -content-hash = "a07dc0d6c4c1b1533e527bb0c2da2162de5fe37ce96b11ab367b0bb3126a5a45" +content-hash = "88bd61a1b5bccb25820a21c7fd0005e6bfdb550637f0159d80e3fcf3ec0c972e" diff --git a/pyproject.toml b/pyproject.toml index fffa49c0ee..7dc6b8f3f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "azure-mgmt-subscription==3.1.1", "azure-mgmt-web==8.0.0", "azure-storage-blob==12.24.1", - "boto3 (==1.35.49)", + "boto3==1.35.49", "botocore==1.35.99", "colorama==0.4.6", "cryptography==44.0.1", @@ -53,7 +53,7 @@ dependencies = [ "pygithub==2.5.0", "python-dateutil (>=2.9.0.post0,<3.0.0)", "pytz==2025.1", - "schema (==0.7.5)", + "schema==0.7.5", "shodan==1.31.0", "slack-sdk==3.34.0", "tabulate==0.9.0", From c0fe90b0c9212c1f4fd460b286b08f0f4a15799a Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Tue, 27 May 2025 17:53:29 +0200 Subject: [PATCH 08/24] chore: add label --- .github/labeler.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index f986a1b00c..5109f0f22b 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -27,6 +27,11 @@ provider/github: - any-glob-to-any-file: "prowler/providers/github/**" - any-glob-to-any-file: "tests/providers/github/**" +provider/iac: + - changed-files: + - any-glob-to-any-file: "prowler/providers/iac/**" + - any-glob-to-any-file: "tests/providers/iac/**" + github_actions: - changed-files: - any-glob-to-any-file: ".github/workflows/*" From 339390820892e8de61b34da74036f43f6b3eff01 Mon Sep 17 00:00:00 2001 From: Andoni Alonso <14891798+andoniaf@users.noreply.github.com> Date: Wed, 28 May 2025 15:54:33 +0200 Subject: [PATCH 09/24] feat(IaC): handling output for IaC Provider POC (#7861) Co-authored-by: MrCloudSec --- .pre-commit-config.yaml | 2 +- prowler/lib/check/models.py | 25 +-- prowler/lib/outputs/compliance/cis/models.py | 12 +- prowler/lib/outputs/finding.py | 12 +- prowler/lib/outputs/html/html.py | 49 +++++- prowler/lib/outputs/ocsf/ocsf.py | 4 +- prowler/providers/azure/models.py | 4 +- prowler/providers/common/provider.py | 1 - prowler/providers/gcp/models.py | 2 +- prowler/providers/iac/iac_provider.py | 152 +++++++++++------- .../kubernetes/services/core/core_service.py | 2 +- 11 files changed, 170 insertions(+), 95 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0382dd901d..5f13fda438 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -115,7 +115,7 @@ repos: - id: safety name: safety description: "Safety is a tool that checks your installed dependencies for known security vulnerabilities" - entry: bash -c 'safety check --ignore 70612,66963,74429' + entry: bash -c 'safety check --ignore 70612,66963,74429,76352,76353' language: system - id: vulture diff --git a/prowler/lib/check/models.py b/prowler/lib/check/models.py index d0dab6ca59..e35dc0eb64 100644 --- a/prowler/lib/check/models.py +++ b/prowler/lib/check/models.py @@ -612,16 +612,9 @@ class CheckReportM365(Check_Report): class CheckReportIAC(Check_Report): """Contains the IAC Check's finding information using Checkov.""" - metadata: dict - check_id: str - check_name: str - check_result: dict - check_result_status: str - file_path: str - file_line_range: list - guideline: str - resource: str - severity: str + resource_name: str + resource_path: str + resource_line_range: str def __init__(self, metadata: dict = {}, finding: dict = {}) -> None: """ @@ -633,15 +626,9 @@ class CheckReportIAC(Check_Report): """ super().__init__(metadata, finding) - self.check_id = finding.get("check_id", "") - self.check_name = finding.get("check_name", "") - self.check_result = finding.get("check_result", {}) - self.check_result_status = self.check_result.get("result", "UNKNOWN") - self.file_path = finding.get("file_path", "") - self.file_line_range = finding.get("file_line_range", []) - self.guideline = finding.get("guideline", "") - self.resource = finding - self.severity = finding.get("severity", "UNKNOWN") + self.resource_name = getattr(finding, "resource", "") + self.resource_path = getattr(finding, "file_path", "") + self.resource_line_range = getattr(finding, "file_line_range", "") @dataclass diff --git a/prowler/lib/outputs/compliance/cis/models.py b/prowler/lib/outputs/compliance/cis/models.py index 1a4764c294..433142e0d9 100644 --- a/prowler/lib/outputs/compliance/cis/models.py +++ b/prowler/lib/outputs/compliance/cis/models.py @@ -16,7 +16,7 @@ class AWSCISModel(BaseModel): Requirements_Id: str Requirements_Description: str Requirements_Attributes_Section: str - Requirements_Attributes_SubSection: Optional[str] + Requirements_Attributes_SubSection: Optional[str] = None Requirements_Attributes_Profile: str Requirements_Attributes_AssessmentStatus: str Requirements_Attributes_Description: str @@ -50,7 +50,7 @@ class AzureCISModel(BaseModel): Requirements_Id: str Requirements_Description: str Requirements_Attributes_Section: str - Requirements_Attributes_SubSection: Optional[str] + Requirements_Attributes_SubSection: Optional[str] = None Requirements_Attributes_Profile: str Requirements_Attributes_AssessmentStatus: str Requirements_Attributes_Description: str @@ -82,7 +82,7 @@ class M365CISModel(BaseModel): Requirements_Id: str Requirements_Description: str Requirements_Attributes_Section: str - Requirements_Attributes_SubSection: Optional[str] + Requirements_Attributes_SubSection: Optional[str] = None Requirements_Attributes_Profile: str Requirements_Attributes_AssessmentStatus: str Requirements_Attributes_Description: str @@ -114,7 +114,7 @@ class GCPCISModel(BaseModel): Requirements_Id: str Requirements_Description: str Requirements_Attributes_Section: str - Requirements_Attributes_SubSection: Optional[str] + Requirements_Attributes_SubSection: Optional[str] = None Requirements_Attributes_Profile: str Requirements_Attributes_AssessmentStatus: str Requirements_Attributes_Description: str @@ -145,8 +145,8 @@ class KubernetesCISModel(BaseModel): Requirements_Id: str Requirements_Description: str Requirements_Attributes_Section: str - Requirements_Attributes_SubSection: Optional[str] - Requirements_Attributes_Profile: str + Requirements_Attributes_SubSection: Optional[str] = None + Requirements_Attributes_Profile: Optional[str] = None Requirements_Attributes_AssessmentStatus: str Requirements_Attributes_Description: str Requirements_Attributes_RationaleStatement: str diff --git a/prowler/lib/outputs/finding.py b/prowler/lib/outputs/finding.py index 2df79a954a..347a31ecd0 100644 --- a/prowler/lib/outputs/finding.py +++ b/prowler/lib/outputs/finding.py @@ -283,12 +283,16 @@ class Finding(BaseModel): output_data["region"] = check_output.location elif provider.type == "iac": - output_data["auth_method"] = "iac" + output_data["auth_method"] = "local" # Until we support remote repos output_data["account_uid"] = "iac" output_data["account_name"] = "iac" - output_data["resource_name"] = check_output.check_name - output_data["resource_uid"] = check_output.check_id - output_data["region"] = check_output.file_path + output_data["resource_name"] = check_output.resource["resource"] + output_data["resource_uid"] = check_output.resource["resource"] + output_data["region"] = check_output.resource_path + output_data["resource_line_range"] = check_output.resource_line_range + output_data["framework"] = ( + check_output.check_metadata.ServiceName + ) # TODO: can we get the framework from the check_output? # check_output Unique ID # TODO: move this to a function diff --git a/prowler/lib/outputs/html/html.py b/prowler/lib/outputs/html/html.py index f775a00d3d..6c54501640 100644 --- a/prowler/lib/outputs/html/html.py +++ b/prowler/lib/outputs/html/html.py @@ -41,7 +41,7 @@ class HTML(Output): {finding_status} {finding.metadata.Severity.value} {finding.metadata.ServiceName} - {finding.region.lower()} + {":".join([finding.resource_metadata['file_path'], "-".join(map(str, finding.resource_metadata['file_line_range']))]) if finding.metadata.Provider == "iac" else finding.region.lower()} {finding.metadata.CheckID.replace("_", "_")} {finding.metadata.CheckTitle} {finding.resource_uid.replace("<", "<").replace(">", ">").replace("_", "_")} @@ -204,7 +204,7 @@ class HTML(Output): Status Severity Service Name - Region + {"File" if provider.type == "iac" else "Region"} Check ID Check Title Resource ID @@ -689,6 +689,51 @@ class HTML(Output): ) return "" + @staticmethod + def get_iac_assessment_summary(provider: Provider) -> str: + """ + get_iac_assessment_summary gets the HTML assessment summary for the provider + + Args: + provider (Provider): the provider object + + Returns: + str: the HTML assessment summary + """ + try: + return f""" +
+
+
+ IAC Assessment Summary +
+
    +
  • + IAC path: {provider.scan_path} +
  • +
+
+
+
+
+
+ IAC Credentials +
+
    +
  • + IAC authentication method: local +
  • +
+
+
""" + except Exception as error: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- {error}" + ) + return "" + @staticmethod def get_assessment_summary(provider: Provider) -> str: """ diff --git a/prowler/lib/outputs/ocsf/ocsf.py b/prowler/lib/outputs/ocsf/ocsf.py index c84a6acf7c..4c47036ef4 100644 --- a/prowler/lib/outputs/ocsf/ocsf.py +++ b/prowler/lib/outputs/ocsf/ocsf.py @@ -3,9 +3,9 @@ from datetime import datetime from typing import List from py_ocsf_models.events.base_event import SeverityID, StatusID -from py_ocsf_models.events.findings.detection_finding import DetectionFinding from py_ocsf_models.events.findings.detection_finding import ( - TypeID as DetectionFindingTypeID, + DetectionFinding, + DetectionFindingTypeID, ) from py_ocsf_models.events.findings.finding import ActivityID, FindingInformation from py_ocsf_models.objects.account import Account, TypeID diff --git a/prowler/providers/azure/models.py b/prowler/providers/azure/models.py index cf0cd4be9b..1745e67214 100644 --- a/prowler/providers/azure/models.py +++ b/prowler/providers/azure/models.py @@ -1,3 +1,5 @@ +from typing import Optional + from pydantic import BaseModel from prowler.config.config import output_file_timestamp @@ -15,7 +17,7 @@ class AzureIdentityInfo(BaseModel): class AzureRegionConfig(BaseModel): name: str = "" - authority: str = None + authority: Optional[str] = None base_url: str = "" credential_scopes: list = [] diff --git a/prowler/providers/common/provider.py b/prowler/providers/common/provider.py index 44e7b067c3..e7f4a74c2f 100644 --- a/prowler/providers/common/provider.py +++ b/prowler/providers/common/provider.py @@ -247,7 +247,6 @@ class Provider(ABC): provider_class( scan_path=arguments.scan_path, config_path=arguments.config_file, - mutelist_path=arguments.mutelist_file, fixer_config=fixer_config, ) diff --git a/prowler/providers/gcp/models.py b/prowler/providers/gcp/models.py index 4abdd6b0ff..a6d925fd2f 100644 --- a/prowler/providers/gcp/models.py +++ b/prowler/providers/gcp/models.py @@ -14,7 +14,7 @@ class GCPOrganization(BaseModel): id: str name: str # TODO: the name needs to be retrieved from another API - display_name: Optional[str] + display_name: Optional[str] = None class GCPProject(BaseModel): diff --git a/prowler/providers/iac/iac_provider.py b/prowler/providers/iac/iac_provider.py index a2e3e502ec..c5d8e1e93c 100644 --- a/prowler/providers/iac/iac_provider.py +++ b/prowler/providers/iac/iac_provider.py @@ -5,6 +5,10 @@ from typing import List from colorama import Fore, Style +from prowler.config.config import ( + default_config_file_path, + load_and_validate_config_file, +) from prowler.lib.check.models import CheckReportIAC from prowler.lib.logger import logger from prowler.lib.utils.utils import print_boxes @@ -22,8 +26,6 @@ class IacProvider(Provider): config_path: str = None, config_content: dict = None, fixer_config: dict = {}, - mutelist_path: str = None, - mutelist_content: dict = None, ): logger.info("Instantiating IAC Provider...") @@ -33,8 +35,18 @@ class IacProvider(Provider): self._session = None self._identity = "prowler" - self._audit_config = config_content if config_content else {} + # Audit Config + if config_content: + self._audit_config = config_content + else: + if not config_path: + config_path = default_config_file_path + self._audit_config = load_and_validate_config_file(self._type, config_path) + + # Fixer Config self._fixer_config = fixer_config + + # Mutelist (not needed for IAC since Checkov has its own mutelist logic) self._mutelist = None self.audit_metadata = Audit_Metadata( @@ -74,6 +86,67 @@ class IacProvider(Provider): """IAC provider doesn't need a session since it uses Checkov directly""" return None + def _process_check(self, finding: dict, check: dict, status: str) -> CheckReportIAC: + """ + Process a single check (failed or passed) and create a CheckReportIAC object. + + Args: + finding: The finding object from Checkov output + check: The individual check data (failed_check or passed_check) + status: The status of the check ("FAIL" or "PASS") + + Returns: + CheckReportIAC: The processed check report + """ + + metadata_dict = { + "Provider": "iac", + "CheckID": check.get("check_id", ""), + "CheckTitle": check.get("check_name", ""), + "CheckType": ["Infrastructure as Code"], + "ServiceName": finding["check_type"], + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": ( + check.get("severity", "low").lower() if check.get("severity") else "low" + ), + "ResourceType": "iac", + "Description": check.get("check_name", ""), + "Risk": "", + "RelatedUrl": ( + check.get("guideline", "") if check.get("guideline") else "" + ), + "Remediation": { + "Code": { + "NativeIaC": "", + "Terraform": "", + "CLI": "", + "Other": "", + }, + "Recommendation": { + "Text": "", + "Url": ( + check.get("guideline", "") if check.get("guideline") else "" + ), + }, + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + } + + # Convert metadata dict to JSON string + metadata = json.dumps(metadata_dict) + + report = CheckReportIAC(metadata=metadata, finding=check) + report.status = status + report.resource_tags = check.get("entity_tags", {}) + report.status_extended = check.get("check_name", "") + if status == "MUTED": + report.muted = True + return report + def run(self) -> List[CheckReportIAC]: return self.run_scan(self.scan_path) @@ -99,65 +172,30 @@ class IacProvider(Provider): reports = [] - # Process failed checks + # If only one framework has findings, the output is a dict, otherwise it's a list of dicts + if isinstance(output, dict): + output = [output] + + # Process all frameworks findings for finding in output: - failed_checks = finding.get("results", {}).get("failed_checks", []) - if not failed_checks: - continue + results = finding.get("results", {}) + # Process failed checks + failed_checks = results.get("failed_checks", []) for failed_check in failed_checks: - # Get severity and ensure it's a valid string - severity = finding.get("severity", "low") - if severity is None: - severity = "low" - severity = str(severity).lower() + report = self._process_check(finding, failed_check, "FAIL") + reports.append(report) - # Create a basic metadata structure for the check - metadata_dict = { - "Provider": "iac", - "CheckID": failed_check.get("check_id", ""), - "CheckTitle": failed_check.get("check_name", ""), - "CheckType": ["Infrastructure as Code"], - "ServiceName": "iac", - "SubServiceName": "", - "ResourceIdTemplate": "", - "Severity": severity, - "ResourceType": "iac", - "Description": failed_check.get("check_name", ""), - "Risk": "", - "RelatedUrl": ( - failed_check.get("guideline", "") - if failed_check.get("guideline") - else "" - ), - "Remediation": { - "Code": { - "NativeIaC": "", - "Terraform": "", - "CLI": "", - "Other": "", - }, - "Recommendation": { - "Text": "", - "Url": ( - failed_check.get("guideline", "") - if failed_check.get("guideline") - else "" - ), - }, - }, - "Categories": [], - "DependsOn": [], - "RelatedTo": [], - "Notes": "", - "GCPProject": failed_check.get("project_id", ""), - } + # Process passed checks + passed_checks = results.get("passed_checks", []) + for passed_check in passed_checks: + report = self._process_check(finding, passed_check, "PASS") + reports.append(report) - # Convert metadata dict to JSON string - metadata = json.dumps(metadata_dict) - - report = CheckReportIAC(metadata=metadata, finding=failed_check) - report.status = "FAIL" + # Process skipped checks (muted) + skipped_checks = results.get("skipped_checks", []) + for skipped_check in skipped_checks: + report = self._process_check(finding, skipped_check, "MUTED") reports.append(report) return reports diff --git a/prowler/providers/kubernetes/services/core/core_service.py b/prowler/providers/kubernetes/services/core/core_service.py index ec6fb48ca8..98138d0d79 100644 --- a/prowler/providers/kubernetes/services/core/core_service.py +++ b/prowler/providers/kubernetes/services/core/core_service.py @@ -171,7 +171,7 @@ class Pod(BaseModel): host_ip: Optional[str] host_pid: Optional[str] host_ipc: Optional[str] - host_network: Optional[str] + host_network: Optional[bool] security_context: Optional[dict] containers: Optional[dict] From e12edc7ea34bbb99d8a6186b3a02987223cd26ba Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Wed, 28 May 2025 16:19:43 +0200 Subject: [PATCH 10/24] chore: handle checkov errors --- prowler/providers/iac/iac_provider.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/prowler/providers/iac/iac_provider.py b/prowler/providers/iac/iac_provider.py index c5d8e1e93c..26b54a5b3d 100644 --- a/prowler/providers/iac/iac_provider.py +++ b/prowler/providers/iac/iac_provider.py @@ -161,14 +161,23 @@ class IacProvider(Provider): text=True, ) + # Log Checkov's error output if any + if process.stderr: + logger.critical( + f"{process.stderr.__class__.__name__} -- {process.stderr}" + ) + sys.exit(1) + try: output = json.loads(process.stdout) if not output: logger.warning("No findings returned from Checkov scan") return [] - except json.JSONDecodeError: - logger.error("Failed to parse IaC scan output as JSON") - return [] + except Exception as error: + logger.critical( + f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}" + ) + sys.exit(1) reports = [] From ba225b3882b2d7e23723a966af5a95aab5cc7390 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Wed, 28 May 2025 17:43:29 +0200 Subject: [PATCH 11/24] chore: add preview label --- prowler/lib/cli/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prowler/lib/cli/parser.py b/prowler/lib/cli/parser.py index 9a68d5ab44..2476a0fa9a 100644 --- a/prowler/lib/cli/parser.py +++ b/prowler/lib/cli/parser.py @@ -36,7 +36,7 @@ Available Cloud Providers: kubernetes Kubernetes Provider github GitHub Provider m365 Microsoft 365 Provider - iac IaC Provider + iac IaC Provider (Preview) nhn NHN Provider (Unofficial) Available components: From 00f031e99bf28ffef8f756ffa7832c48a9b87913 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Thu, 29 May 2025 10:21:32 +0200 Subject: [PATCH 12/24] fix: gcp tests --- prowler/providers/gcp/models.py | 2 +- .../gcp/services/cloudstorage/cloudstorage_service.py | 2 +- prowler/providers/gcp/services/kms/kms_service.py | 4 ++-- tests/providers/gcp/lib/mutelist/gcp_mutelist_test.py | 2 +- .../iam_service_account_unused_test.py | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/prowler/providers/gcp/models.py b/prowler/providers/gcp/models.py index a6d925fd2f..491fe896d1 100644 --- a/prowler/providers/gcp/models.py +++ b/prowler/providers/gcp/models.py @@ -18,7 +18,7 @@ class GCPOrganization(BaseModel): class GCPProject(BaseModel): - number: str + number: int id: str name: str organization: Optional[GCPOrganization] = None diff --git a/prowler/providers/gcp/services/cloudstorage/cloudstorage_service.py b/prowler/providers/gcp/services/cloudstorage/cloudstorage_service.py index 0e2e74d27c..317d30217b 100644 --- a/prowler/providers/gcp/services/cloudstorage/cloudstorage_service.py +++ b/prowler/providers/gcp/services/cloudstorage/cloudstorage_service.py @@ -60,4 +60,4 @@ class Bucket(BaseModel): uniform_bucket_level_access: bool public: bool project_id: str - retention_policy: Optional[dict] + retention_policy: Optional[dict] = None diff --git a/prowler/providers/gcp/services/kms/kms_service.py b/prowler/providers/gcp/services/kms/kms_service.py index 3ac498d2ba..77a4c0c427 100644 --- a/prowler/providers/gcp/services/kms/kms_service.py +++ b/prowler/providers/gcp/services/kms/kms_service.py @@ -143,8 +143,8 @@ class CriptoKey(BaseModel): id: str name: str location: str - rotation_period: Optional[str] - next_rotation_time: Optional[str] + rotation_period: Optional[str] = None + next_rotation_time: Optional[str] = None key_ring: str members: list = [] project_id: str diff --git a/tests/providers/gcp/lib/mutelist/gcp_mutelist_test.py b/tests/providers/gcp/lib/mutelist/gcp_mutelist_test.py index 0471c5f0f1..ced1fcc2c6 100644 --- a/tests/providers/gcp/lib/mutelist/gcp_mutelist_test.py +++ b/tests/providers/gcp/lib/mutelist/gcp_mutelist_test.py @@ -89,7 +89,7 @@ class TestGCPMutelist: account_uid="project_1", region="test-region", resource_uid="test_resource", - resource_tags=[], + resource_tags={}, muted=False, ) diff --git a/tests/providers/gcp/services/iam/iam_service_account_unused/iam_service_account_unused_test.py b/tests/providers/gcp/services/iam/iam_service_account_unused/iam_service_account_unused_test.py index d76200734d..a50e1f80e4 100644 --- a/tests/providers/gcp/services/iam/iam_service_account_unused/iam_service_account_unused_test.py +++ b/tests/providers/gcp/services/iam/iam_service_account_unused/iam_service_account_unused_test.py @@ -96,7 +96,7 @@ class Test_iam_service_account_unused: assert result[0].resource_id == iam_client.service_accounts[0].email assert result[0].project_id == GCP_PROJECT_ID assert result[0].location == GCP_US_CENTER1_LOCATION - assert result[0].resource == iam_client.service_accounts[0] + assert result[0].resource == iam_client.service_accounts[0].model_dump() def test_iam_service_account_unused_mix(self): iam_client = mock.MagicMock() @@ -168,7 +168,7 @@ class Test_iam_service_account_unused: assert result[0].resource_id == iam_client.service_accounts[0].email assert result[0].project_id == GCP_PROJECT_ID assert result[0].location == GCP_US_CENTER1_LOCATION - assert result[0].resource == iam_client.service_accounts[0] + assert result[0].resource == iam_client.service_accounts[0].model_dump() assert result[1].status == "FAIL" assert ( @@ -178,4 +178,4 @@ class Test_iam_service_account_unused: assert result[1].resource_id == iam_client.service_accounts[1].email assert result[1].project_id == GCP_PROJECT_ID assert result[1].location == GCP_US_CENTER1_LOCATION - assert result[1].resource == iam_client.service_accounts[1] + assert result[1].resource == iam_client.service_accounts[1].model_dump() From d6fbd14baafa97c5fca5fbf235c04056345e2d2a Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Thu, 29 May 2025 10:23:08 +0200 Subject: [PATCH 13/24] fix: tests k8s --- prowler/providers/kubernetes/services/rbac/rbac_service.py | 6 +++--- .../kubernetes/lib/mutelist/kubernetes_mutelist_test.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/prowler/providers/kubernetes/services/rbac/rbac_service.py b/prowler/providers/kubernetes/services/rbac/rbac_service.py index db8550c318..3bc5d2beec 100644 --- a/prowler/providers/kubernetes/services/rbac/rbac_service.py +++ b/prowler/providers/kubernetes/services/rbac/rbac_service.py @@ -162,9 +162,9 @@ class RoleBinding(BaseModel): class Rule(BaseModel): - apiGroups: Optional[List[str]] - resources: Optional[List[str]] - verbs: Optional[List[str]] + apiGroups: Optional[List[str]] = None + resources: Optional[List[str]] = None + verbs: Optional[List[str]] = None class Role(BaseModel): diff --git a/tests/providers/kubernetes/lib/mutelist/kubernetes_mutelist_test.py b/tests/providers/kubernetes/lib/mutelist/kubernetes_mutelist_test.py index 7cc1ae394d..9b43c48376 100644 --- a/tests/providers/kubernetes/lib/mutelist/kubernetes_mutelist_test.py +++ b/tests/providers/kubernetes/lib/mutelist/kubernetes_mutelist_test.py @@ -153,7 +153,7 @@ class TestKubernetesMutelist: account_uid="cluster_1", region="test-region", resource_uid="test_resource", - resource_tags=[], + resource_tags={}, muted=False, ) From 0a30ecaaaf6ef61d7c1ab64df7a518cee871e499 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Thu, 29 May 2025 10:35:13 +0200 Subject: [PATCH 14/24] fix: azure tests --- prowler/lib/outputs/asff/asff.py | 2 +- .../compliance/aws_well_architected/models.py | 2 +- prowler/lib/outputs/compliance/cis/models.py | 6 ++--- .../lib/outputs/compliance/generic/models.py | 10 ++++---- .../outputs/compliance/kisa_ismsp/models.py | 8 +++---- .../compliance/prowler_threatscore/models.py | 8 +++---- prowler/providers/aws/lib/arn/models.py | 2 +- .../aws/services/account/account_service.py | 6 ++--- .../providers/aws/services/acm/acm_service.py | 2 +- .../services/apigateway/apigateway_service.py | 8 +++---- .../services/awslambda/awslambda_service.py | 8 +++---- .../aws/services/backup/backup_service.py | 12 +++++----- .../aws/services/bedrock/bedrock_service.py | 4 ++-- .../services/cloudfront/cloudfront_service.py | 22 ++++++++--------- .../services/cloudwatch/cloudwatch_service.py | 6 ++--- .../codeartifact/codeartifact_service.py | 2 +- .../services/codebuild/codebuild_service.py | 22 ++++++++--------- .../azure/services/entra/entra_service.py | 24 +++++++++---------- .../azure/lib/mutelist/azure_mutelist_test.py | 4 ++-- 19 files changed, 79 insertions(+), 79 deletions(-) diff --git a/prowler/lib/outputs/asff/asff.py b/prowler/lib/outputs/asff/asff.py index ceb81e9ca2..1f4ce98328 100644 --- a/prowler/lib/outputs/asff/asff.py +++ b/prowler/lib/outputs/asff/asff.py @@ -279,7 +279,7 @@ class Resource(BaseModel): Id: str Partition: str Region: str - Tags: Optional[dict] + Tags: Optional[dict] = None @validator("Tags", pre=True, always=True) def tags_cannot_be_empty_dict(tags): diff --git a/prowler/lib/outputs/compliance/aws_well_architected/models.py b/prowler/lib/outputs/compliance/aws_well_architected/models.py index 3cb75fce9b..f9aaffd7da 100644 --- a/prowler/lib/outputs/compliance/aws_well_architected/models.py +++ b/prowler/lib/outputs/compliance/aws_well_architected/models.py @@ -19,7 +19,7 @@ class AWSWellArchitectedModel(BaseModel): Requirements_Attributes_WellArchitectedQuestionId: str Requirements_Attributes_WellArchitectedPracticeId: str Requirements_Attributes_Section: str - Requirements_Attributes_SubSection: Optional[str] + Requirements_Attributes_SubSection: Optional[str] = None Requirements_Attributes_LevelOfRisk: str Requirements_Attributes_AssessmentMethod: str Requirements_Attributes_Description: str diff --git a/prowler/lib/outputs/compliance/cis/models.py b/prowler/lib/outputs/compliance/cis/models.py index 433142e0d9..1dacbff9ed 100644 --- a/prowler/lib/outputs/compliance/cis/models.py +++ b/prowler/lib/outputs/compliance/cis/models.py @@ -25,9 +25,9 @@ class AWSCISModel(BaseModel): Requirements_Attributes_RemediationProcedure: str Requirements_Attributes_AuditProcedure: str Requirements_Attributes_AdditionalInformation: str - Requirements_Attributes_DefaultValue: Optional[ - str - ] # TODO Optional for now since it's not present in the CIS 1.5, 2.0 and 3.0 AWS benchmark + Requirements_Attributes_DefaultValue: Optional[str] = ( + None # TODO Optional for now since it's not present in the CIS 1.5, 2.0 and 3.0 AWS benchmark + ) Requirements_Attributes_References: str Status: str StatusExtended: str diff --git a/prowler/lib/outputs/compliance/generic/models.py b/prowler/lib/outputs/compliance/generic/models.py index 8cd38ec49a..0de2d41b69 100644 --- a/prowler/lib/outputs/compliance/generic/models.py +++ b/prowler/lib/outputs/compliance/generic/models.py @@ -15,11 +15,11 @@ class GenericComplianceModel(BaseModel): AssessmentDate: str Requirements_Id: str Requirements_Description: str - Requirements_Attributes_Section: Optional[str] - Requirements_Attributes_SubSection: Optional[str] - Requirements_Attributes_SubGroup: Optional[str] - Requirements_Attributes_Service: Optional[str] - Requirements_Attributes_Type: Optional[str] + Requirements_Attributes_Section: Optional[str] = None + Requirements_Attributes_SubSection: Optional[str] = None + Requirements_Attributes_SubGroup: Optional[str] = None + Requirements_Attributes_Service: Optional[str] = None + Requirements_Attributes_Type: Optional[str] = None Status: str StatusExtended: str ResourceId: str diff --git a/prowler/lib/outputs/compliance/kisa_ismsp/models.py b/prowler/lib/outputs/compliance/kisa_ismsp/models.py index 98b1f00a78..2249397c66 100644 --- a/prowler/lib/outputs/compliance/kisa_ismsp/models.py +++ b/prowler/lib/outputs/compliance/kisa_ismsp/models.py @@ -19,10 +19,10 @@ class AWSKISAISMSPModel(BaseModel): Requirements_Attributes_Domain: str Requirements_Attributes_Subdomain: str Requirements_Attributes_Section: str - Requirements_Attributes_AuditChecklist: Optional[list[str]] - Requirements_Attributes_RelatedRegulations: Optional[list[str]] - Requirements_Attributes_AuditEvidence: Optional[list[str]] - Requirements_Attributes_NonComplianceCases: Optional[list[str]] + Requirements_Attributes_AuditChecklist: Optional[list[str]] = None + Requirements_Attributes_RelatedRegulations: Optional[list[str]] = None + Requirements_Attributes_AuditEvidence: Optional[list[str]] = None + Requirements_Attributes_NonComplianceCases: Optional[list[str]] = None Status: str StatusExtended: str ResourceId: str diff --git a/prowler/lib/outputs/compliance/prowler_threatscore/models.py b/prowler/lib/outputs/compliance/prowler_threatscore/models.py index c8ac0dd783..d48b054ffe 100644 --- a/prowler/lib/outputs/compliance/prowler_threatscore/models.py +++ b/prowler/lib/outputs/compliance/prowler_threatscore/models.py @@ -17,7 +17,7 @@ class ProwlerThreatScoreAWSModel(BaseModel): Requirements_Description: str Requirements_Attributes_Title: str Requirements_Attributes_Section: str - Requirements_Attributes_SubSection: Optional[str] + Requirements_Attributes_SubSection: Optional[str] = None Requirements_Attributes_AttributeDescription: str Requirements_Attributes_AdditionalInformation: str Requirements_Attributes_LevelOfRisk: int @@ -44,7 +44,7 @@ class ProwlerThreatScoreAzureModel(BaseModel): Requirements_Description: str Requirements_Attributes_Title: str Requirements_Attributes_Section: str - Requirements_Attributes_SubSection: Optional[str] + Requirements_Attributes_SubSection: Optional[str] = None Requirements_Attributes_AttributeDescription: str Requirements_Attributes_AdditionalInformation: str Requirements_Attributes_LevelOfRisk: int @@ -71,7 +71,7 @@ class ProwlerThreatScoreGCPModel(BaseModel): Requirements_Description: str Requirements_Attributes_Title: str Requirements_Attributes_Section: str - Requirements_Attributes_SubSection: Optional[str] + Requirements_Attributes_SubSection: Optional[str] = None Requirements_Attributes_AttributeDescription: str Requirements_Attributes_AdditionalInformation: str Requirements_Attributes_LevelOfRisk: int @@ -98,7 +98,7 @@ class ProwlerThreatScoreM365Model(BaseModel): Requirements_Description: str Requirements_Attributes_Title: str Requirements_Attributes_Section: str - Requirements_Attributes_SubSection: Optional[str] + Requirements_Attributes_SubSection: Optional[str] = None Requirements_Attributes_AttributeDescription: str Requirements_Attributes_AdditionalInformation: str Requirements_Attributes_LevelOfRisk: int diff --git a/prowler/providers/aws/lib/arn/models.py b/prowler/providers/aws/lib/arn/models.py index ba0be84452..038c72bb58 100644 --- a/prowler/providers/aws/lib/arn/models.py +++ b/prowler/providers/aws/lib/arn/models.py @@ -10,7 +10,7 @@ class ARN(BaseModel): arn: str partition: str service: str - region: Optional[str] # In IAM ARN's do not have region + region: Optional[str] = None # In IAM ARN's do not have region account_id: str resource: str resource_type: str diff --git a/prowler/providers/aws/services/account/account_service.py b/prowler/providers/aws/services/account/account_service.py index 786d827e1a..baaa7d91df 100644 --- a/prowler/providers/aws/services/account/account_service.py +++ b/prowler/providers/aws/services/account/account_service.py @@ -101,6 +101,6 @@ class Account(AWSService): class Contact(BaseModel): type: str - email: Optional[str] - name: Optional[str] - phone_number: Optional[str] + email: Optional[str] = None + name: Optional[str] = None + phone_number: Optional[str] = None diff --git a/prowler/providers/aws/services/acm/acm_service.py b/prowler/providers/aws/services/acm/acm_service.py index cb9996831c..85242ea904 100644 --- a/prowler/providers/aws/services/acm/acm_service.py +++ b/prowler/providers/aws/services/acm/acm_service.py @@ -111,5 +111,5 @@ class Certificate(BaseModel): tags: Optional[list] = [] expiration_days: int in_use: bool - transparency_logging: Optional[bool] + transparency_logging: Optional[bool] = None region: str diff --git a/prowler/providers/aws/services/apigateway/apigateway_service.py b/prowler/providers/aws/services/apigateway/apigateway_service.py index c1525e0ea1..ad1f8ff6a5 100644 --- a/prowler/providers/aws/services/apigateway/apigateway_service.py +++ b/prowler/providers/aws/services/apigateway/apigateway_service.py @@ -224,11 +224,11 @@ class Stage(BaseModel): arn: str logging: bool client_certificate: bool - waf: Optional[str] + waf: Optional[str] = None tags: Optional[list] = [] - tracing_enabled: Optional[bool] - cache_enabled: Optional[bool] - cache_data_encrypted: Optional[bool] + tracing_enabled: Optional[bool] = None + cache_enabled: Optional[bool] = None + cache_data_encrypted: Optional[bool] = None class PathResourceMethods(BaseModel): diff --git a/prowler/providers/aws/services/awslambda/awslambda_service.py b/prowler/providers/aws/services/awslambda/awslambda_service.py index 32150b7313..f5a9b95c95 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_service.py +++ b/prowler/providers/aws/services/awslambda/awslambda_service.py @@ -196,12 +196,12 @@ class Function(BaseModel): name: str arn: str security_groups: list - runtime: Optional[str] + runtime: Optional[str] = None environment: dict = None region: str - policy: dict = None + policy: dict = {} code: LambdaCode = None url_config: URLConfig = None - vpc_id: Optional[str] - subnet_ids: Optional[set] + vpc_id: Optional[str] = None + subnet_ids: Optional[set] = None tags: Optional[list] = [] diff --git a/prowler/providers/aws/services/backup/backup_service.py b/prowler/providers/aws/services/backup/backup_service.py index 92aea28672..cd50dfd1c1 100644 --- a/prowler/providers/aws/services/backup/backup_service.py +++ b/prowler/providers/aws/services/backup/backup_service.py @@ -227,7 +227,7 @@ class BackupVault(BaseModel): locked: bool min_retention_days: int = None max_retention_days: int = None - tags: Optional[list] + tags: Optional[list] = None class BackupPlan(BaseModel): @@ -236,17 +236,17 @@ class BackupPlan(BaseModel): region: str name: str version_id: str - last_execution_date: Optional[datetime] + last_execution_date: Optional[datetime] = None advanced_settings: list - tags: Optional[list] + tags: Optional[list] = None class BackupReportPlan(BaseModel): arn: str region: str name: str - last_attempted_execution_date: Optional[datetime] - last_successful_execution_date: Optional[datetime] + last_attempted_execution_date: Optional[datetime] = None + last_successful_execution_date: Optional[datetime] = None class RecoveryPoint(BaseModel): @@ -256,4 +256,4 @@ class RecoveryPoint(BaseModel): backup_vault_name: str encrypted: bool backup_vault_region: str - tags: Optional[list] + tags: Optional[list] = None diff --git a/prowler/providers/aws/services/bedrock/bedrock_service.py b/prowler/providers/aws/services/bedrock/bedrock_service.py index 118e0cb8ae..e8e4d19c51 100644 --- a/prowler/providers/aws/services/bedrock/bedrock_service.py +++ b/prowler/providers/aws/services/bedrock/bedrock_service.py @@ -116,7 +116,7 @@ class Guardrail(BaseModel): region: str tags: Optional[list] = [] sensitive_information_filter: bool = False - prompt_attack_filter_strength: Optional[str] + prompt_attack_filter_strength: Optional[str] = None class BedrockAgent(AWSService): @@ -169,6 +169,6 @@ class Agent(BaseModel): id: str name: str arn: str - guardrail_id: Optional[str] + guardrail_id: Optional[str] = None region: str tags: Optional[list] = [] diff --git a/prowler/providers/aws/services/cloudfront/cloudfront_service.py b/prowler/providers/aws/services/cloudfront/cloudfront_service.py index f6d8f63365..9537b041fb 100644 --- a/prowler/providers/aws/services/cloudfront/cloudfront_service.py +++ b/prowler/providers/aws/services/cloudfront/cloudfront_service.py @@ -186,7 +186,7 @@ class SSLSupportMethod(Enum): class DefaultCacheConfigBehaviour(BaseModel): - realtime_log_config_arn: Optional[str] + realtime_log_config_arn: Optional[str] = None viewer_protocol_policy: ViewerProtocolPolicy field_level_encryption_id: str @@ -196,8 +196,8 @@ class Origin(BaseModel): domain_name: str origin_protocol_policy: str origin_ssl_protocols: list[str] - origin_access_control: Optional[str] - s3_origin_config: Optional[dict] + origin_access_control: Optional[str] = None + s3_origin_config: Optional[dict] = None class Distribution(BaseModel): @@ -207,14 +207,14 @@ class Distribution(BaseModel): id: str region: str logging_enabled: bool = False - default_cache_config: Optional[DefaultCacheConfigBehaviour] - geo_restriction_type: Optional[GeoRestrictionType] + default_cache_config: Optional[DefaultCacheConfigBehaviour] = None + geo_restriction_type: Optional[GeoRestrictionType] = None origins: list[Origin] web_acl_id: str = "" - default_certificate: Optional[bool] - default_root_object: Optional[str] - viewer_protocol_policy: Optional[str] + default_certificate: Optional[bool] = None + default_root_object: Optional[str] = None + viewer_protocol_policy: Optional[str] = None tags: Optional[list] = [] - origin_failover: Optional[bool] - ssl_support_method: Optional[SSLSupportMethod] - certificate: Optional[str] + origin_failover: Optional[bool] = None + ssl_support_method: Optional[SSLSupportMethod] = None + certificate: Optional[str] = None diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py index ac05ebae10..9ced454f92 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py @@ -278,8 +278,8 @@ class Logs(AWSService): class MetricAlarm(BaseModel): arn: str name: str - metric: Optional[str] - name_space: Optional[str] + metric: Optional[str] = None + name_space: Optional[str] = None region: str tags: Optional[list] = [] alarm_actions: list @@ -310,7 +310,7 @@ class MetricFilter(BaseModel): name: str metric: str pattern: str - log_group: Optional[LogGroup] + log_group: Optional[LogGroup] = None region: str diff --git a/prowler/providers/aws/services/codeartifact/codeartifact_service.py b/prowler/providers/aws/services/codeartifact/codeartifact_service.py index c48b791390..28e83619e0 100644 --- a/prowler/providers/aws/services/codeartifact/codeartifact_service.py +++ b/prowler/providers/aws/services/codeartifact/codeartifact_service.py @@ -246,7 +246,7 @@ class Package(BaseModel): """Details of a package""" name: str - namespace: Optional[str] + namespace: Optional[str] = None format: str origin_configuration: OriginConfiguration latest_version: LatestPackageVersion diff --git a/prowler/providers/aws/services/codebuild/codebuild_service.py b/prowler/providers/aws/services/codebuild/codebuild_service.py index a1e00790bb..24ffb3de53 100644 --- a/prowler/providers/aws/services/codebuild/codebuild_service.py +++ b/prowler/providers/aws/services/codebuild/codebuild_service.py @@ -211,15 +211,15 @@ class Project(BaseModel): name: str arn: str region: str - last_build: Optional[Build] - last_invoked_time: Optional[datetime.datetime] - buildspec: Optional[str] - source: Optional[Source] + last_build: Optional[Build] = None + last_invoked_time: Optional[datetime.datetime] = None + buildspec: Optional[str] = None + source: Optional[Source] = None secondary_sources: Optional[list[Source]] = [] - environment_variables: Optional[List[EnvironmentVariable]] - s3_logs: Optional[s3Logs] - cloudwatch_logs: Optional[CloudWatchLogs] - tags: Optional[list] + environment_variables: Optional[List[EnvironmentVariable]] = [] + s3_logs: Optional[s3Logs] = None + cloudwatch_logs: Optional[CloudWatchLogs] = None + tags: Optional[list] = [] class ExportConfig(BaseModel): @@ -233,6 +233,6 @@ class ReportGroup(BaseModel): arn: str name: str region: str - status: Optional[str] - export_config: Optional[ExportConfig] - tags: Optional[list] + status: Optional[str] = None + export_config: Optional[ExportConfig] = None + tags: Optional[list] = [] diff --git a/prowler/providers/azure/services/entra/entra_service.py b/prowler/providers/azure/services/entra/entra_service.py index 45f0c0128c..5f81d620c0 100644 --- a/prowler/providers/azure/services/entra/entra_service.py +++ b/prowler/providers/azure/services/entra/entra_service.py @@ -367,12 +367,12 @@ class User(BaseModel): class DefaultUserRolePermissions(BaseModel): - allowed_to_create_apps: Optional[bool] - allowed_to_create_security_groups: Optional[bool] - allowed_to_create_tenants: Optional[bool] - allowed_to_read_bitlocker_keys_for_owned_device: Optional[bool] - allowed_to_read_other_users: Optional[bool] - odata_type: Optional[str] + allowed_to_create_apps: Optional[bool] = None + allowed_to_create_security_groups: Optional[bool] = None + allowed_to_create_tenants: Optional[bool] = None + allowed_to_read_bitlocker_keys_for_owned_device: Optional[bool] = None + allowed_to_read_other_users: Optional[bool] = None + odata_type: Optional[str] = None permission_grant_policies_assigned: Optional[List[str]] = None @@ -380,20 +380,20 @@ class AuthorizationPolicy(BaseModel): id: str name: str description: str - default_user_role_permissions: Optional[DefaultUserRolePermissions] + default_user_role_permissions: Optional[DefaultUserRolePermissions] = None guest_invite_settings: str guest_user_role_id: UUID class SettingValue(BaseModel): - name: Optional[str] - odata_type: Optional[str] - value: Optional[str] + name: Optional[str] = None + odata_type: Optional[str] = None + value: Optional[str] = None class GroupSetting(BaseModel): - name: Optional[str] - template_id: Optional[str] + name: Optional[str] = None + template_id: Optional[str] = None settings: List[SettingValue] diff --git a/tests/providers/azure/lib/mutelist/azure_mutelist_test.py b/tests/providers/azure/lib/mutelist/azure_mutelist_test.py index 3488beb54f..6b3715bcc4 100644 --- a/tests/providers/azure/lib/mutelist/azure_mutelist_test.py +++ b/tests/providers/azure/lib/mutelist/azure_mutelist_test.py @@ -63,7 +63,7 @@ class TestAzureMutelist: finding.location = "West Europe" finding.status = "FAIL" finding.resource_name = "test_resource" - finding.resource_tags = [] + finding.resource_tags = {} finding.subscription = "subscription_1" assert mutelist.is_finding_muted(finding) @@ -91,7 +91,7 @@ class TestAzureMutelist: account_uid="subscription_1", region="subscription_1", resource_uid="test_resource", - resource_tags=[], + resource_tags={}, muted=False, ) From 34078df65b295d59a625989c4e10438158100f4d Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Thu, 29 May 2025 10:42:23 +0200 Subject: [PATCH 15/24] fix: nhn tests --- tests/providers/nhn/lib/mutelist/nhn_mutelist_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/providers/nhn/lib/mutelist/nhn_mutelist_test.py b/tests/providers/nhn/lib/mutelist/nhn_mutelist_test.py index dcdc17b2d9..da78ad8b5a 100644 --- a/tests/providers/nhn/lib/mutelist/nhn_mutelist_test.py +++ b/tests/providers/nhn/lib/mutelist/nhn_mutelist_test.py @@ -62,7 +62,7 @@ class TestNHNMutelist: finding.status = "FAIL" finding.resource_name = "test_resource" finding.location = "test_region" - finding.resource_tags = [] + finding.resource_tags = {} assert mutelist.is_finding_muted(finding) @@ -89,7 +89,7 @@ class TestNHNMutelist: account_uid="resource_1", region="test_region", resource_uid="test_resource", - resource_tags=[], + resource_tags={}, muted=False, ) From cafb7d544e339f172ac36361053d87dae33ea1b2 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Thu, 29 May 2025 10:43:48 +0200 Subject: [PATCH 16/24] chore: add github action for iac --- .github/workflows/sdk-pull-request.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/sdk-pull-request.yml b/.github/workflows/sdk-pull-request.yml index 1fa3e9c592..e7f28ba1d8 100644 --- a/.github/workflows/sdk-pull-request.yml +++ b/.github/workflows/sdk-pull-request.yml @@ -212,6 +212,21 @@ jobs: run: | poetry run pytest -n auto --cov=./prowler/providers/m365 --cov-report=xml:m365_coverage.xml tests/providers/m365 + # Test IaC + - name: IaC - Check if any file has changed + id: iac-changed-files + uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5 + with: + files: | + ./prowler/providers/iac/** + ./tests/providers/iac/** + .poetry.lock + + - name: IaC - Test + if: steps.iac-changed-files.outputs.any_changed == 'true' + run: | + poetry run pytest -n auto --cov=./prowler/providers/iac --cov-report=xml:iac_coverage.xml tests/providers/iac + # Common Tests - name: Lib - Test if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true' From 66e0b6a0b2d94983d823fd970488ee0faaf77f42 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Thu, 29 May 2025 10:47:11 +0200 Subject: [PATCH 17/24] fix: m365 test --- prowler/providers/m365/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/prowler/providers/m365/models.py b/prowler/providers/m365/models.py index f77bc8ee4c..ca9d74c44d 100644 --- a/prowler/providers/m365/models.py +++ b/prowler/providers/m365/models.py @@ -1,3 +1,5 @@ +from typing import Optional + from pydantic import BaseModel from prowler.config.config import output_file_timestamp @@ -16,7 +18,7 @@ class M365IdentityInfo(BaseModel): class M365RegionConfig(BaseModel): name: str = "" - authority: str = None + authority: Optional[str] = None base_url: str = "" credential_scopes: list = [] From fdff4f4cde2bc289bd689b5fc1e214723e70c135 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Thu, 29 May 2025 10:50:46 +0200 Subject: [PATCH 18/24] fix: use v1 basemodel of pydantic --- prowler/lib/check/compliance_models.py | 2 +- prowler/lib/check/models.py | 2 +- prowler/lib/outputs/asff/asff.py | 2 +- .../lib/outputs/compliance/aws_well_architected/models.py | 2 +- prowler/lib/outputs/compliance/cis/models.py | 2 +- prowler/lib/outputs/compliance/ens/models.py | 2 +- prowler/lib/outputs/compliance/generic/models.py | 2 +- prowler/lib/outputs/compliance/iso27001/models.py | 2 +- prowler/lib/outputs/compliance/kisa_ismsp/models.py | 2 +- prowler/lib/outputs/compliance/mitre_attack/models.py | 2 +- .../lib/outputs/compliance/prowler_threatscore/models.py | 2 +- prowler/lib/outputs/finding.py | 2 +- prowler/providers/aws/lib/arn/models.py | 2 +- .../aws/services/accessanalyzer/accessanalyzer_service.py | 2 +- prowler/providers/aws/services/account/account_service.py | 2 +- prowler/providers/aws/services/acm/acm_service.py | 2 +- .../providers/aws/services/apigateway/apigateway_service.py | 2 +- .../aws/services/apigatewayv2/apigatewayv2_service.py | 2 +- .../providers/aws/services/appstream/appstream_service.py | 2 +- prowler/providers/aws/services/appsync/appsync_service.py | 2 +- prowler/providers/aws/services/athena/athena_service.py | 2 +- .../aws/services/autoscaling/autoscaling_service.py | 2 +- .../providers/aws/services/awslambda/awslambda_service.py | 2 +- prowler/providers/aws/services/backup/backup_service.py | 2 +- prowler/providers/aws/services/bedrock/bedrock_service.py | 2 +- .../aws/services/cloudformation/cloudformation_service.py | 2 +- .../providers/aws/services/cloudfront/cloudfront_service.py | 2 +- .../providers/aws/services/cloudtrail/cloudtrail_service.py | 2 +- .../providers/aws/services/cloudwatch/cloudwatch_service.py | 2 +- .../aws/services/codeartifact/codeartifact_service.py | 2 +- .../providers/aws/services/codebuild/codebuild_service.py | 2 +- prowler/providers/aws/services/cognito/cognito_service.py | 2 +- prowler/providers/aws/services/config/config_service.py | 2 +- prowler/providers/aws/services/datasync/datasync_service.py | 2 +- .../aws/services/directconnect/directconnect_service.py | 2 +- .../services/directoryservice/directoryservice_service.py | 2 +- prowler/providers/aws/services/dlm/dlm_service.py | 2 +- prowler/providers/aws/services/dms/dms_service.py | 2 +- .../providers/aws/services/documentdb/documentdb_service.py | 2 +- prowler/providers/aws/services/drs/drs_service.py | 2 +- prowler/providers/aws/services/dynamodb/dynamodb_service.py | 2 +- prowler/providers/aws/services/ec2/ec2_service.py | 2 +- prowler/providers/aws/services/ecr/ecr_service.py | 2 +- prowler/providers/aws/services/ecs/ecs_service.py | 2 +- prowler/providers/aws/services/efs/efs_service.py | 2 +- prowler/providers/aws/services/eks/eks_service.py | 2 +- .../aws/services/elasticache/elasticache_service.py | 2 +- .../services/elasticbeanstalk/elasticbeanstalk_service.py | 2 +- prowler/providers/aws/services/elb/elb_service.py | 2 +- prowler/providers/aws/services/elbv2/elbv2_service.py | 2 +- prowler/providers/aws/services/emr/emr_service.py | 2 +- .../aws/services/eventbridge/eventbridge_service.py | 2 +- prowler/providers/aws/services/firehose/firehose_service.py | 2 +- prowler/providers/aws/services/fms/fms_service.py | 2 +- prowler/providers/aws/services/fsx/fsx_service.py | 2 +- prowler/providers/aws/services/glacier/glacier_service.py | 2 +- .../services/globalaccelerator/globalaccelerator_service.py | 2 +- prowler/providers/aws/services/glue/glue_service.py | 2 +- .../providers/aws/services/guardduty/guardduty_service.py | 2 +- prowler/providers/aws/services/iam/iam_service.py | 2 +- .../providers/aws/services/inspector2/inspector2_service.py | 2 +- prowler/providers/aws/services/kafka/kafka_service.py | 2 +- prowler/providers/aws/services/kinesis/kinesis_service.py | 2 +- prowler/providers/aws/services/kms/kms_service.py | 2 +- .../providers/aws/services/lightsail/lightsail_service.py | 2 +- prowler/providers/aws/services/macie/macie_service.py | 2 +- prowler/providers/aws/services/memorydb/memorydb_service.py | 2 +- prowler/providers/aws/services/mq/mq_service.py | 2 +- prowler/providers/aws/services/neptune/neptune_service.py | 2 +- .../aws/services/networkfirewall/networkfirewall_service.py | 2 +- .../providers/aws/services/opensearch/opensearch_service.py | 2 +- .../aws/services/organizations/organizations_service.py | 2 +- prowler/providers/aws/services/rds/rds_service.py | 2 +- prowler/providers/aws/services/redshift/redshift_service.py | 2 +- .../services/resourceexplorer2/resourceexplorer2_service.py | 2 +- prowler/providers/aws/services/route53/route53_service.py | 2 +- prowler/providers/aws/services/s3/s3_service.py | 2 +- .../providers/aws/services/sagemaker/sagemaker_service.py | 2 +- .../aws/services/secretsmanager/secretsmanager_service.py | 2 +- .../aws/services/securityhub/securityhub_service.py | 2 +- .../aws/services/servicecatalog/servicecatalog_service.py | 2 +- prowler/providers/aws/services/ses/ses_service.py | 2 +- prowler/providers/aws/services/shield/shield_service.py | 2 +- prowler/providers/aws/services/sns/sns_service.py | 2 +- prowler/providers/aws/services/sqs/sqs_service.py | 2 +- prowler/providers/aws/services/ssm/ssm_service.py | 2 +- .../aws/services/ssmincidents/ssmincidents_service.py | 2 +- .../aws/services/stepfunctions/stepfunctions_service.py | 2 +- .../aws/services/storagegateway/storagegateway_service.py | 2 +- prowler/providers/aws/services/transfer/transfer_service.py | 2 +- .../aws/services/trustedadvisor/trustedadvisor_service.py | 2 +- prowler/providers/aws/services/vpc/vpc_service.py | 2 +- prowler/providers/aws/services/waf/waf_service.py | 2 +- prowler/providers/aws/services/wafv2/wafv2_service.py | 2 +- .../aws/services/wellarchitected/wellarchitected_service.py | 2 +- .../providers/aws/services/workspaces/workspaces_service.py | 2 +- prowler/providers/azure/models.py | 2 +- .../providers/azure/services/aisearch/aisearch_service.py | 2 +- .../azure/services/appinsights/appinsights_service.py | 2 +- .../providers/azure/services/defender/defender_service.py | 2 +- prowler/providers/azure/services/entra/entra_service.py | 2 +- prowler/providers/common/models.py | 2 +- prowler/providers/gcp/models.py | 2 +- prowler/providers/gcp/services/apikeys/apikeys_service.py | 2 +- prowler/providers/gcp/services/bigquery/bigquery_service.py | 2 +- .../cloudresourcemanager/cloudresourcemanager_service.py | 2 +- prowler/providers/gcp/services/cloudsql/cloudsql_service.py | 2 +- .../gcp/services/cloudstorage/cloudstorage_service.py | 2 +- prowler/providers/gcp/services/compute/compute_service.py | 2 +- prowler/providers/gcp/services/dataproc/dataproc_service.py | 2 +- prowler/providers/gcp/services/dns/dns_service.py | 2 +- prowler/providers/gcp/services/gke/gke_service.py | 2 +- prowler/providers/gcp/services/iam/iam_service.py | 2 +- prowler/providers/gcp/services/kms/kms_service.py | 2 +- prowler/providers/gcp/services/logging/logging_service.py | 2 +- .../providers/gcp/services/monitoring/monitoring_service.py | 2 +- .../gcp/services/serviceusage/serviceusage_service.py | 2 +- prowler/providers/github/models.py | 2 +- .../github/services/organization/organization_service.py | 2 +- .../github/services/repository/repository_service.py | 2 +- prowler/providers/kubernetes/services/core/core_service.py | 2 +- prowler/providers/kubernetes/services/rbac/rbac_service.py | 2 +- prowler/providers/m365/models.py | 2 +- .../m365/services/admincenter/admincenter_service.py | 2 +- .../providers/m365/services/defender/defender_service.py | 2 +- prowler/providers/m365/services/entra/entra_service.py | 2 +- .../providers/m365/services/exchange/exchange_service.py | 2 +- prowler/providers/m365/services/purview/purview_service.py | 2 +- .../m365/services/sharepoint/sharepoint_service.py | 2 +- prowler/providers/m365/services/teams/teams_service.py | 2 +- prowler/providers/nhn/models.py | 2 +- prowler/providers/nhn/services/compute/compute_service.py | 2 +- prowler/providers/nhn/services/network/network_service.py | 2 +- .../iam_service_account_unused_test.py | 6 +++--- 134 files changed, 136 insertions(+), 136 deletions(-) diff --git a/prowler/lib/check/compliance_models.py b/prowler/lib/check/compliance_models.py index 8ed8ab45bb..b6ab382101 100644 --- a/prowler/lib/check/compliance_models.py +++ b/prowler/lib/check/compliance_models.py @@ -3,7 +3,7 @@ import sys from enum import Enum from typing import Optional, Union -from pydantic import BaseModel, ValidationError, root_validator +from pydantic.v1 import BaseModel, ValidationError, root_validator from prowler.lib.check.utils import list_compliance_modules from prowler.lib.logger import logger diff --git a/prowler/lib/check/models.py b/prowler/lib/check/models.py index e35dc0eb64..fc1bf32cd5 100644 --- a/prowler/lib/check/models.py +++ b/prowler/lib/check/models.py @@ -7,7 +7,7 @@ from dataclasses import asdict, dataclass, is_dataclass from enum import Enum from typing import Any, Dict, Optional, Set -from pydantic import BaseModel, ValidationError, validator +from pydantic.v1 import BaseModel, ValidationError, validator from prowler.config.config import Provider from prowler.lib.check.compliance_models import Compliance diff --git a/prowler/lib/outputs/asff/asff.py b/prowler/lib/outputs/asff/asff.py index 1f4ce98328..a5216b36ee 100644 --- a/prowler/lib/outputs/asff/asff.py +++ b/prowler/lib/outputs/asff/asff.py @@ -2,7 +2,7 @@ from json import dump from os import SEEK_SET from typing import Optional -from pydantic import BaseModel, validator +from pydantic.v1 import BaseModel, validator from prowler.config.config import prowler_version, timestamp_utc from prowler.lib.logger import logger diff --git a/prowler/lib/outputs/compliance/aws_well_architected/models.py b/prowler/lib/outputs/compliance/aws_well_architected/models.py index f9aaffd7da..2a2abbb06c 100644 --- a/prowler/lib/outputs/compliance/aws_well_architected/models.py +++ b/prowler/lib/outputs/compliance/aws_well_architected/models.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel class AWSWellArchitectedModel(BaseModel): diff --git a/prowler/lib/outputs/compliance/cis/models.py b/prowler/lib/outputs/compliance/cis/models.py index 1dacbff9ed..9bf51c6430 100644 --- a/prowler/lib/outputs/compliance/cis/models.py +++ b/prowler/lib/outputs/compliance/cis/models.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel class AWSCISModel(BaseModel): diff --git a/prowler/lib/outputs/compliance/ens/models.py b/prowler/lib/outputs/compliance/ens/models.py index 6ff2b9e52f..8f9a6ad03a 100644 --- a/prowler/lib/outputs/compliance/ens/models.py +++ b/prowler/lib/outputs/compliance/ens/models.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel class AWSENSModel(BaseModel): diff --git a/prowler/lib/outputs/compliance/generic/models.py b/prowler/lib/outputs/compliance/generic/models.py index 0de2d41b69..900066478d 100644 --- a/prowler/lib/outputs/compliance/generic/models.py +++ b/prowler/lib/outputs/compliance/generic/models.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel class GenericComplianceModel(BaseModel): diff --git a/prowler/lib/outputs/compliance/iso27001/models.py b/prowler/lib/outputs/compliance/iso27001/models.py index 16e97a178d..af40196079 100644 --- a/prowler/lib/outputs/compliance/iso27001/models.py +++ b/prowler/lib/outputs/compliance/iso27001/models.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel class AWSISO27001Model(BaseModel): diff --git a/prowler/lib/outputs/compliance/kisa_ismsp/models.py b/prowler/lib/outputs/compliance/kisa_ismsp/models.py index 2249397c66..b4ba1d6c76 100644 --- a/prowler/lib/outputs/compliance/kisa_ismsp/models.py +++ b/prowler/lib/outputs/compliance/kisa_ismsp/models.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel class AWSKISAISMSPModel(BaseModel): diff --git a/prowler/lib/outputs/compliance/mitre_attack/models.py b/prowler/lib/outputs/compliance/mitre_attack/models.py index e01ffeeae7..4b304dd151 100644 --- a/prowler/lib/outputs/compliance/mitre_attack/models.py +++ b/prowler/lib/outputs/compliance/mitre_attack/models.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel class AWSMitreAttackModel(BaseModel): diff --git a/prowler/lib/outputs/compliance/prowler_threatscore/models.py b/prowler/lib/outputs/compliance/prowler_threatscore/models.py index d48b054ffe..363ea40f3f 100644 --- a/prowler/lib/outputs/compliance/prowler_threatscore/models.py +++ b/prowler/lib/outputs/compliance/prowler_threatscore/models.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel class ProwlerThreatScoreAWSModel(BaseModel): diff --git a/prowler/lib/outputs/finding.py b/prowler/lib/outputs/finding.py index 347a31ecd0..21235d7d6f 100644 --- a/prowler/lib/outputs/finding.py +++ b/prowler/lib/outputs/finding.py @@ -3,7 +3,7 @@ from datetime import datetime from types import SimpleNamespace from typing import Optional, Union -from pydantic import BaseModel, Field, ValidationError +from pydantic.v1 import BaseModel, Field, ValidationError from prowler.config.config import prowler_version from prowler.lib.check.models import ( diff --git a/prowler/providers/aws/lib/arn/models.py b/prowler/providers/aws/lib/arn/models.py index 038c72bb58..1f8f923e29 100644 --- a/prowler/providers/aws/lib/arn/models.py +++ b/prowler/providers/aws/lib/arn/models.py @@ -1,7 +1,7 @@ import os from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.providers.aws.exceptions.exceptions import AWSIAMRoleARNMissingFieldsError diff --git a/prowler/providers/aws/services/accessanalyzer/accessanalyzer_service.py b/prowler/providers/aws/services/accessanalyzer/accessanalyzer_service.py index fbe6f3826e..1732ba7e50 100644 --- a/prowler/providers/aws/services/accessanalyzer/accessanalyzer_service.py +++ b/prowler/providers/aws/services/accessanalyzer/accessanalyzer_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/account/account_service.py b/prowler/providers/aws/services/account/account_service.py index baaa7d91df..08000112ca 100644 --- a/prowler/providers/aws/services/account/account_service.py +++ b/prowler/providers/aws/services/account/account_service.py @@ -2,7 +2,7 @@ from typing import Optional from venv import logger from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.providers.aws.lib.service.service import AWSService diff --git a/prowler/providers/aws/services/acm/acm_service.py b/prowler/providers/aws/services/acm/acm_service.py index 85242ea904..b21737cd9a 100644 --- a/prowler/providers/aws/services/acm/acm_service.py +++ b/prowler/providers/aws/services/acm/acm_service.py @@ -1,7 +1,7 @@ from datetime import datetime from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/apigateway/apigateway_service.py b/prowler/providers/aws/services/apigateway/apigateway_service.py index ad1f8ff6a5..f61a502791 100644 --- a/prowler/providers/aws/services/apigateway/apigateway_service.py +++ b/prowler/providers/aws/services/apigateway/apigateway_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/apigatewayv2/apigatewayv2_service.py b/prowler/providers/aws/services/apigatewayv2/apigatewayv2_service.py index cb8d88e5ef..e8ea2583cb 100644 --- a/prowler/providers/aws/services/apigatewayv2/apigatewayv2_service.py +++ b/prowler/providers/aws/services/apigatewayv2/apigatewayv2_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/appstream/appstream_service.py b/prowler/providers/aws/services/appstream/appstream_service.py index b1ee202bb9..f88496ec38 100644 --- a/prowler/providers/aws/services/appstream/appstream_service.py +++ b/prowler/providers/aws/services/appstream/appstream_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/appsync/appsync_service.py b/prowler/providers/aws/services/appsync/appsync_service.py index cb39aa8f5d..576f533241 100644 --- a/prowler/providers/aws/services/appsync/appsync_service.py +++ b/prowler/providers/aws/services/appsync/appsync_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/athena/athena_service.py b/prowler/providers/aws/services/athena/athena_service.py index ea8ae707e4..ba145bfd81 100644 --- a/prowler/providers/aws/services/athena/athena_service.py +++ b/prowler/providers/aws/services/athena/athena_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_service.py b/prowler/providers/aws/services/autoscaling/autoscaling_service.py index fe2671f88b..eae261344e 100644 --- a/prowler/providers/aws/services/autoscaling/autoscaling_service.py +++ b/prowler/providers/aws/services/autoscaling/autoscaling_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/awslambda/awslambda_service.py b/prowler/providers/aws/services/awslambda/awslambda_service.py index f5a9b95c95..aea2bec272 100644 --- a/prowler/providers/aws/services/awslambda/awslambda_service.py +++ b/prowler/providers/aws/services/awslambda/awslambda_service.py @@ -7,7 +7,7 @@ from typing import Any, Optional import requests from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/backup/backup_service.py b/prowler/providers/aws/services/backup/backup_service.py index cd50dfd1c1..4320d42c0b 100644 --- a/prowler/providers/aws/services/backup/backup_service.py +++ b/prowler/providers/aws/services/backup/backup_service.py @@ -2,7 +2,7 @@ from datetime import datetime from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/bedrock/bedrock_service.py b/prowler/providers/aws/services/bedrock/bedrock_service.py index e8e4d19c51..c00fc61ac0 100644 --- a/prowler/providers/aws/services/bedrock/bedrock_service.py +++ b/prowler/providers/aws/services/bedrock/bedrock_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/cloudformation/cloudformation_service.py b/prowler/providers/aws/services/cloudformation/cloudformation_service.py index 38caf5033f..fb8a491486 100644 --- a/prowler/providers/aws/services/cloudformation/cloudformation_service.py +++ b/prowler/providers/aws/services/cloudformation/cloudformation_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/cloudfront/cloudfront_service.py b/prowler/providers/aws/services/cloudfront/cloudfront_service.py index 9537b041fb..b7f85113b4 100644 --- a/prowler/providers/aws/services/cloudfront/cloudfront_service.py +++ b/prowler/providers/aws/services/cloudfront/cloudfront_service.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py b/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py index b3f21522dc..d146dc14c7 100644 --- a/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py +++ b/prowler/providers/aws/services/cloudtrail/cloudtrail_service.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py b/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py index 9ced454f92..29ac103867 100644 --- a/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py +++ b/prowler/providers/aws/services/cloudwatch/cloudwatch_service.py @@ -3,7 +3,7 @@ from datetime import datetime, timezone from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/codeartifact/codeartifact_service.py b/prowler/providers/aws/services/codeartifact/codeartifact_service.py index 28e83619e0..f3d312a531 100644 --- a/prowler/providers/aws/services/codeartifact/codeartifact_service.py +++ b/prowler/providers/aws/services/codeartifact/codeartifact_service.py @@ -2,7 +2,7 @@ from enum import Enum from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/codebuild/codebuild_service.py b/prowler/providers/aws/services/codebuild/codebuild_service.py index 24ffb3de53..598e619d40 100644 --- a/prowler/providers/aws/services/codebuild/codebuild_service.py +++ b/prowler/providers/aws/services/codebuild/codebuild_service.py @@ -1,7 +1,7 @@ import datetime from typing import List, Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/cognito/cognito_service.py b/prowler/providers/aws/services/cognito/cognito_service.py index 70075cad2e..813ef8decf 100644 --- a/prowler/providers/aws/services/cognito/cognito_service.py +++ b/prowler/providers/aws/services/cognito/cognito_service.py @@ -1,7 +1,7 @@ from datetime import datetime from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/config/config_service.py b/prowler/providers/aws/services/config/config_service.py index a53c61caa1..443cee2233 100644 --- a/prowler/providers/aws/services/config/config_service.py +++ b/prowler/providers/aws/services/config/config_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/datasync/datasync_service.py b/prowler/providers/aws/services/datasync/datasync_service.py index 9a75855d75..690a265be6 100644 --- a/prowler/providers/aws/services/datasync/datasync_service.py +++ b/prowler/providers/aws/services/datasync/datasync_service.py @@ -1,7 +1,7 @@ from typing import Dict, List, Optional from botocore.exceptions import ClientError -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/directconnect/directconnect_service.py b/prowler/providers/aws/services/directconnect/directconnect_service.py index 3f79087523..bf6b9e7eb7 100644 --- a/prowler/providers/aws/services/directconnect/directconnect_service.py +++ b/prowler/providers/aws/services/directconnect/directconnect_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/directoryservice/directoryservice_service.py b/prowler/providers/aws/services/directoryservice/directoryservice_service.py index 0beea818e2..6d57708620 100644 --- a/prowler/providers/aws/services/directoryservice/directoryservice_service.py +++ b/prowler/providers/aws/services/directoryservice/directoryservice_service.py @@ -3,7 +3,7 @@ from enum import Enum from typing import Optional, Union from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/dlm/dlm_service.py b/prowler/providers/aws/services/dlm/dlm_service.py index e06992660a..1d6fff9b5a 100644 --- a/prowler/providers/aws/services/dlm/dlm_service.py +++ b/prowler/providers/aws/services/dlm/dlm_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.aws.lib.service.service import AWSService diff --git a/prowler/providers/aws/services/dms/dms_service.py b/prowler/providers/aws/services/dms/dms_service.py index 97764dd439..1aaa1e3762 100644 --- a/prowler/providers/aws/services/dms/dms_service.py +++ b/prowler/providers/aws/services/dms/dms_service.py @@ -1,7 +1,7 @@ import json from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/documentdb/documentdb_service.py b/prowler/providers/aws/services/documentdb/documentdb_service.py index 3beaf901d3..39674c1998 100644 --- a/prowler/providers/aws/services/documentdb/documentdb_service.py +++ b/prowler/providers/aws/services/documentdb/documentdb_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/drs/drs_service.py b/prowler/providers/aws/services/drs/drs_service.py index c3e99a6345..e49cbeff34 100644 --- a/prowler/providers/aws/services/drs/drs_service.py +++ b/prowler/providers/aws/services/drs/drs_service.py @@ -1,5 +1,5 @@ from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/dynamodb/dynamodb_service.py b/prowler/providers/aws/services/dynamodb/dynamodb_service.py index ad6c959955..110d9d4c9c 100644 --- a/prowler/providers/aws/services/dynamodb/dynamodb_service.py +++ b/prowler/providers/aws/services/dynamodb/dynamodb_service.py @@ -2,7 +2,7 @@ import json from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/ec2/ec2_service.py b/prowler/providers/aws/services/ec2/ec2_service.py index 2336f43478..9559346db4 100644 --- a/prowler/providers/aws/services/ec2/ec2_service.py +++ b/prowler/providers/aws/services/ec2/ec2_service.py @@ -3,7 +3,7 @@ from ipaddress import IPv4Address, IPv6Address, ip_address from typing import Optional, Union from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/ecr/ecr_service.py b/prowler/providers/aws/services/ecr/ecr_service.py index c892ad1fca..a09969725a 100644 --- a/prowler/providers/aws/services/ecr/ecr_service.py +++ b/prowler/providers/aws/services/ecr/ecr_service.py @@ -3,7 +3,7 @@ from json import loads from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/ecs/ecs_service.py b/prowler/providers/aws/services/ecs/ecs_service.py index f01dbbb667..811f2a26b1 100644 --- a/prowler/providers/aws/services/ecs/ecs_service.py +++ b/prowler/providers/aws/services/ecs/ecs_service.py @@ -1,7 +1,7 @@ from re import sub from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/efs/efs_service.py b/prowler/providers/aws/services/efs/efs_service.py index 38b7f03073..3d82198284 100644 --- a/prowler/providers/aws/services/efs/efs_service.py +++ b/prowler/providers/aws/services/efs/efs_service.py @@ -2,7 +2,7 @@ import json from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/eks/eks_service.py b/prowler/providers/aws/services/eks/eks_service.py index 4731887a02..e7b3ec389c 100644 --- a/prowler/providers/aws/services/eks/eks_service.py +++ b/prowler/providers/aws/services/eks/eks_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/elasticache/elasticache_service.py b/prowler/providers/aws/services/elasticache/elasticache_service.py index 6e11a85bc6..47d54f704a 100644 --- a/prowler/providers/aws/services/elasticache/elasticache_service.py +++ b/prowler/providers/aws/services/elasticache/elasticache_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py index 3f8faa0e64..0005177bdb 100644 --- a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/elb/elb_service.py b/prowler/providers/aws/services/elb/elb_service.py index 85d56d4946..fb8f21982f 100644 --- a/prowler/providers/aws/services/elb/elb_service.py +++ b/prowler/providers/aws/services/elb/elb_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/elbv2/elbv2_service.py b/prowler/providers/aws/services/elbv2/elbv2_service.py index 96889b54e4..c52110869f 100644 --- a/prowler/providers/aws/services/elbv2/elbv2_service.py +++ b/prowler/providers/aws/services/elbv2/elbv2_service.py @@ -1,7 +1,7 @@ from typing import Dict, Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/emr/emr_service.py b/prowler/providers/aws/services/emr/emr_service.py index e1fb7c15da..df2a5176c4 100644 --- a/prowler/providers/aws/services/emr/emr_service.py +++ b/prowler/providers/aws/services/emr/emr_service.py @@ -2,7 +2,7 @@ from enum import Enum from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/eventbridge/eventbridge_service.py b/prowler/providers/aws/services/eventbridge/eventbridge_service.py index 58b717d05d..28972e49c9 100644 --- a/prowler/providers/aws/services/eventbridge/eventbridge_service.py +++ b/prowler/providers/aws/services/eventbridge/eventbridge_service.py @@ -2,7 +2,7 @@ import json from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/firehose/firehose_service.py b/prowler/providers/aws/services/firehose/firehose_service.py index 9ef76d2fbb..d496da9c4e 100644 --- a/prowler/providers/aws/services/firehose/firehose_service.py +++ b/prowler/providers/aws/services/firehose/firehose_service.py @@ -2,7 +2,7 @@ from enum import Enum from typing import Dict, List, Optional from botocore.client import ClientError -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/fms/fms_service.py b/prowler/providers/aws/services/fms/fms_service.py index 443fde4a47..1875283c63 100644 --- a/prowler/providers/aws/services/fms/fms_service.py +++ b/prowler/providers/aws/services/fms/fms_service.py @@ -1,5 +1,5 @@ from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/fsx/fsx_service.py b/prowler/providers/aws/services/fsx/fsx_service.py index 7366204eca..27df140016 100644 --- a/prowler/providers/aws/services/fsx/fsx_service.py +++ b/prowler/providers/aws/services/fsx/fsx_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/glacier/glacier_service.py b/prowler/providers/aws/services/glacier/glacier_service.py index 9281f7485b..96e1d5be95 100644 --- a/prowler/providers/aws/services/glacier/glacier_service.py +++ b/prowler/providers/aws/services/glacier/glacier_service.py @@ -2,7 +2,7 @@ import json from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/globalaccelerator/globalaccelerator_service.py b/prowler/providers/aws/services/globalaccelerator/globalaccelerator_service.py index 706056c5b3..0a767cafed 100644 --- a/prowler/providers/aws/services/globalaccelerator/globalaccelerator_service.py +++ b/prowler/providers/aws/services/globalaccelerator/globalaccelerator_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/glue/glue_service.py b/prowler/providers/aws/services/glue/glue_service.py index 21e7fd4907..4376b19f6a 100644 --- a/prowler/providers/aws/services/glue/glue_service.py +++ b/prowler/providers/aws/services/glue/glue_service.py @@ -2,7 +2,7 @@ import json from typing import Dict, List, Optional from botocore.exceptions import ClientError -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/guardduty/guardduty_service.py b/prowler/providers/aws/services/guardduty/guardduty_service.py index f67f0881e6..c267771209 100644 --- a/prowler/providers/aws/services/guardduty/guardduty_service.py +++ b/prowler/providers/aws/services/guardduty/guardduty_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/iam/iam_service.py b/prowler/providers/aws/services/iam/iam_service.py index dafd86017c..82612f22f0 100644 --- a/prowler/providers/aws/services/iam/iam_service.py +++ b/prowler/providers/aws/services/iam/iam_service.py @@ -3,7 +3,7 @@ from datetime import datetime from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.config.config import encoding_format_utf_8 from prowler.lib.logger import logger diff --git a/prowler/providers/aws/services/inspector2/inspector2_service.py b/prowler/providers/aws/services/inspector2/inspector2_service.py index 52a84fa9d5..cc6dac7413 100644 --- a/prowler/providers/aws/services/inspector2/inspector2_service.py +++ b/prowler/providers/aws/services/inspector2/inspector2_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.aws.lib.service.service import AWSService diff --git a/prowler/providers/aws/services/kafka/kafka_service.py b/prowler/providers/aws/services/kafka/kafka_service.py index 96a1418490..4197e543c4 100644 --- a/prowler/providers/aws/services/kafka/kafka_service.py +++ b/prowler/providers/aws/services/kafka/kafka_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/kinesis/kinesis_service.py b/prowler/providers/aws/services/kinesis/kinesis_service.py index cc5a7c1495..455d0fff0d 100644 --- a/prowler/providers/aws/services/kinesis/kinesis_service.py +++ b/prowler/providers/aws/services/kinesis/kinesis_service.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Dict, List, Optional -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/kms/kms_service.py b/prowler/providers/aws/services/kms/kms_service.py index 1dc4988842..4269e5ccf8 100644 --- a/prowler/providers/aws/services/kms/kms_service.py +++ b/prowler/providers/aws/services/kms/kms_service.py @@ -1,7 +1,7 @@ import json from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/lightsail/lightsail_service.py b/prowler/providers/aws/services/lightsail/lightsail_service.py index 56c137e21f..ce364a2238 100644 --- a/prowler/providers/aws/services/lightsail/lightsail_service.py +++ b/prowler/providers/aws/services/lightsail/lightsail_service.py @@ -1,6 +1,6 @@ from typing import Dict, List -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/macie/macie_service.py b/prowler/providers/aws/services/macie/macie_service.py index a11c78f940..f1ebd5cac3 100644 --- a/prowler/providers/aws/services/macie/macie_service.py +++ b/prowler/providers/aws/services/macie/macie_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.aws.lib.service.service import AWSService diff --git a/prowler/providers/aws/services/memorydb/memorydb_service.py b/prowler/providers/aws/services/memorydb/memorydb_service.py index 2fbe096940..6bfbb255e3 100644 --- a/prowler/providers/aws/services/memorydb/memorydb_service.py +++ b/prowler/providers/aws/services/memorydb/memorydb_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/mq/mq_service.py b/prowler/providers/aws/services/mq/mq_service.py index b97e51f35c..cc51c2a7bb 100644 --- a/prowler/providers/aws/services/mq/mq_service.py +++ b/prowler/providers/aws/services/mq/mq_service.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Dict, List -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/neptune/neptune_service.py b/prowler/providers/aws/services/neptune/neptune_service.py index 1c48d713ba..1ac631e3db 100644 --- a/prowler/providers/aws/services/neptune/neptune_service.py +++ b/prowler/providers/aws/services/neptune/neptune_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/networkfirewall/networkfirewall_service.py b/prowler/providers/aws/services/networkfirewall/networkfirewall_service.py index 7ed0bcfbc4..18f9e1eed7 100644 --- a/prowler/providers/aws/services/networkfirewall/networkfirewall_service.py +++ b/prowler/providers/aws/services/networkfirewall/networkfirewall_service.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/opensearch/opensearch_service.py b/prowler/providers/aws/services/opensearch/opensearch_service.py index 6e7cf2619e..b4602ba69c 100644 --- a/prowler/providers/aws/services/opensearch/opensearch_service.py +++ b/prowler/providers/aws/services/opensearch/opensearch_service.py @@ -1,7 +1,7 @@ from json import JSONDecodeError, loads from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/organizations/organizations_service.py b/prowler/providers/aws/services/organizations/organizations_service.py index b3580fbebd..78f5b134bd 100644 --- a/prowler/providers/aws/services/organizations/organizations_service.py +++ b/prowler/providers/aws/services/organizations/organizations_service.py @@ -2,7 +2,7 @@ import json from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/rds/rds_service.py b/prowler/providers/aws/services/rds/rds_service.py index 9e2a921bdd..2f8941c8b3 100644 --- a/prowler/providers/aws/services/rds/rds_service.py +++ b/prowler/providers/aws/services/rds/rds_service.py @@ -2,7 +2,7 @@ from datetime import datetime from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/redshift/redshift_service.py b/prowler/providers/aws/services/redshift/redshift_service.py index b0f4a06421..2a587f01b7 100644 --- a/prowler/providers/aws/services/redshift/redshift_service.py +++ b/prowler/providers/aws/services/redshift/redshift_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_service.py b/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_service.py index 8fe0b413f6..c828dab731 100644 --- a/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_service.py +++ b/prowler/providers/aws/services/resourceexplorer2/resourceexplorer2_service.py @@ -1,5 +1,5 @@ from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/route53/route53_service.py b/prowler/providers/aws/services/route53/route53_service.py index 4cc744c985..2e0eeb4499 100644 --- a/prowler/providers/aws/services/route53/route53_service.py +++ b/prowler/providers/aws/services/route53/route53_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/s3/s3_service.py b/prowler/providers/aws/services/s3/s3_service.py index 4ff091052c..3dec3fc441 100644 --- a/prowler/providers/aws/services/s3/s3_service.py +++ b/prowler/providers/aws/services/s3/s3_service.py @@ -2,7 +2,7 @@ import json from typing import Dict, List, Optional from botocore.client import ClientError -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/sagemaker/sagemaker_service.py b/prowler/providers/aws/services/sagemaker/sagemaker_service.py index 8001b742cd..3e52bf0a82 100644 --- a/prowler/providers/aws/services/sagemaker/sagemaker_service.py +++ b/prowler/providers/aws/services/sagemaker/sagemaker_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/secretsmanager/secretsmanager_service.py b/prowler/providers/aws/services/secretsmanager/secretsmanager_service.py index 12f23d7ff0..8a85d33502 100644 --- a/prowler/providers/aws/services/secretsmanager/secretsmanager_service.py +++ b/prowler/providers/aws/services/secretsmanager/secretsmanager_service.py @@ -2,7 +2,7 @@ import json from datetime import datetime, timezone from typing import Dict, List, Optional -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/securityhub/securityhub_service.py b/prowler/providers/aws/services/securityhub/securityhub_service.py index 3485113571..0799c6e048 100644 --- a/prowler/providers/aws/services/securityhub/securityhub_service.py +++ b/prowler/providers/aws/services/securityhub/securityhub_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/servicecatalog/servicecatalog_service.py b/prowler/providers/aws/services/servicecatalog/servicecatalog_service.py index 2950d234b2..94efb5f623 100644 --- a/prowler/providers/aws/services/servicecatalog/servicecatalog_service.py +++ b/prowler/providers/aws/services/servicecatalog/servicecatalog_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/ses/ses_service.py b/prowler/providers/aws/services/ses/ses_service.py index 57a24083d3..ff19f829e6 100644 --- a/prowler/providers/aws/services/ses/ses_service.py +++ b/prowler/providers/aws/services/ses/ses_service.py @@ -1,7 +1,7 @@ from json import loads from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/shield/shield_service.py b/prowler/providers/aws/services/shield/shield_service.py index 06129a94a6..baeb768b37 100644 --- a/prowler/providers/aws/services/shield/shield_service.py +++ b/prowler/providers/aws/services/shield/shield_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.aws.lib.service.service import AWSService diff --git a/prowler/providers/aws/services/sns/sns_service.py b/prowler/providers/aws/services/sns/sns_service.py index 2eb93ee792..766967269f 100644 --- a/prowler/providers/aws/services/sns/sns_service.py +++ b/prowler/providers/aws/services/sns/sns_service.py @@ -2,7 +2,7 @@ from json import loads from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/sqs/sqs_service.py b/prowler/providers/aws/services/sqs/sqs_service.py index 5371ab9c98..598cd625a4 100644 --- a/prowler/providers/aws/services/sqs/sqs_service.py +++ b/prowler/providers/aws/services/sqs/sqs_service.py @@ -2,7 +2,7 @@ from json import loads from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/ssm/ssm_service.py b/prowler/providers/aws/services/ssm/ssm_service.py index 3f8c62d6f5..33f1187993 100644 --- a/prowler/providers/aws/services/ssm/ssm_service.py +++ b/prowler/providers/aws/services/ssm/ssm_service.py @@ -4,7 +4,7 @@ from enum import Enum from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/ssmincidents/ssmincidents_service.py b/prowler/providers/aws/services/ssmincidents/ssmincidents_service.py index fd6dce7b62..90ed978cd9 100644 --- a/prowler/providers/aws/services/ssmincidents/ssmincidents_service.py +++ b/prowler/providers/aws/services/ssmincidents/ssmincidents_service.py @@ -1,5 +1,5 @@ from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/stepfunctions/stepfunctions_service.py b/prowler/providers/aws/services/stepfunctions/stepfunctions_service.py index 76f041a897..7580c88357 100644 --- a/prowler/providers/aws/services/stepfunctions/stepfunctions_service.py +++ b/prowler/providers/aws/services/stepfunctions/stepfunctions_service.py @@ -3,7 +3,7 @@ from enum import Enum from typing import Dict, List, Optional from botocore.exceptions import ClientError -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/storagegateway/storagegateway_service.py b/prowler/providers/aws/services/storagegateway/storagegateway_service.py index b55c5c7f4d..5f8b6392e3 100644 --- a/prowler/providers/aws/services/storagegateway/storagegateway_service.py +++ b/prowler/providers/aws/services/storagegateway/storagegateway_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/transfer/transfer_service.py b/prowler/providers/aws/services/transfer/transfer_service.py index e1d037fdae..f86e195a31 100644 --- a/prowler/providers/aws/services/transfer/transfer_service.py +++ b/prowler/providers/aws/services/transfer/transfer_service.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Dict, List -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py index 4dd899c0ca..bad30341b2 100644 --- a/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py +++ b/prowler/providers/aws/services/trustedadvisor/trustedadvisor_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.aws.lib.service.service import AWSService diff --git a/prowler/providers/aws/services/vpc/vpc_service.py b/prowler/providers/aws/services/vpc/vpc_service.py index f2a4298dd8..50df596d6b 100644 --- a/prowler/providers/aws/services/vpc/vpc_service.py +++ b/prowler/providers/aws/services/vpc/vpc_service.py @@ -2,7 +2,7 @@ import json from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/waf/waf_service.py b/prowler/providers/aws/services/waf/waf_service.py index b0521fd992..b1fda19c50 100644 --- a/prowler/providers/aws/services/waf/waf_service.py +++ b/prowler/providers/aws/services/waf/waf_service.py @@ -1,6 +1,6 @@ from typing import Dict, List, Optional -from pydantic import BaseModel, Field +from pydantic.v1 import BaseModel, Field from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/wafv2/wafv2_service.py b/prowler/providers/aws/services/wafv2/wafv2_service.py index c867691b77..6a9d3ca5b8 100644 --- a/prowler/providers/aws/services/wafv2/wafv2_service.py +++ b/prowler/providers/aws/services/wafv2/wafv2_service.py @@ -2,7 +2,7 @@ from enum import Enum from typing import Optional from botocore.exceptions import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/wellarchitected/wellarchitected_service.py b/prowler/providers/aws/services/wellarchitected/wellarchitected_service.py index 3e8951d8c0..8bc87afdf5 100644 --- a/prowler/providers/aws/services/wellarchitected/wellarchitected_service.py +++ b/prowler/providers/aws/services/wellarchitected/wellarchitected_service.py @@ -1,7 +1,7 @@ from typing import Optional from botocore.client import ClientError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/aws/services/workspaces/workspaces_service.py b/prowler/providers/aws/services/workspaces/workspaces_service.py index 48617897ff..319fa8cf73 100644 --- a/prowler/providers/aws/services/workspaces/workspaces_service.py +++ b/prowler/providers/aws/services/workspaces/workspaces_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.lib.scan_filters.scan_filters import is_resource_filtered diff --git a/prowler/providers/azure/models.py b/prowler/providers/azure/models.py index 1745e67214..752d1372c6 100644 --- a/prowler/providers/azure/models.py +++ b/prowler/providers/azure/models.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.config.config import output_file_timestamp from prowler.providers.common.models import ProviderOutputOptions diff --git a/prowler/providers/azure/services/aisearch/aisearch_service.py b/prowler/providers/azure/services/aisearch/aisearch_service.py index c1d530798c..2324be227d 100644 --- a/prowler/providers/azure/services/aisearch/aisearch_service.py +++ b/prowler/providers/azure/services/aisearch/aisearch_service.py @@ -1,5 +1,5 @@ from azure.mgmt.search import SearchManagementClient -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider diff --git a/prowler/providers/azure/services/appinsights/appinsights_service.py b/prowler/providers/azure/services/appinsights/appinsights_service.py index f101a5e40b..aae9dbf9b0 100644 --- a/prowler/providers/azure/services/appinsights/appinsights_service.py +++ b/prowler/providers/azure/services/appinsights/appinsights_service.py @@ -1,5 +1,5 @@ from azure.mgmt.applicationinsights import ApplicationInsightsManagementClient -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider diff --git a/prowler/providers/azure/services/defender/defender_service.py b/prowler/providers/azure/services/defender/defender_service.py index 6b66eaa1c8..697df3f692 100644 --- a/prowler/providers/azure/services/defender/defender_service.py +++ b/prowler/providers/azure/services/defender/defender_service.py @@ -7,7 +7,7 @@ from azure.core.exceptions import ( ResourceNotFoundError, ) from azure.mgmt.security import SecurityCenter -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider diff --git a/prowler/providers/azure/services/entra/entra_service.py b/prowler/providers/azure/services/entra/entra_service.py index 5f81d620c0..547a9db459 100644 --- a/prowler/providers/azure/services/entra/entra_service.py +++ b/prowler/providers/azure/services/entra/entra_service.py @@ -3,7 +3,7 @@ from typing import List, Optional from uuid import UUID from msgraph import GraphServiceClient -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.azure.azure_provider import AzureProvider diff --git a/prowler/providers/common/models.py b/prowler/providers/common/models.py index 47c9ebd2fe..ea70252f0a 100644 --- a/prowler/providers/common/models.py +++ b/prowler/providers/common/models.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from os import makedirs from os.path import isdir -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.providers.common.provider import Provider diff --git a/prowler/providers/gcp/models.py b/prowler/providers/gcp/models.py index 491fe896d1..e1688b7973 100644 --- a/prowler/providers/gcp/models.py +++ b/prowler/providers/gcp/models.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.config.config import output_file_timestamp from prowler.providers.common.models import ProviderOutputOptions diff --git a/prowler/providers/gcp/services/apikeys/apikeys_service.py b/prowler/providers/gcp/services/apikeys/apikeys_service.py index 73dc504ede..65379c761f 100644 --- a/prowler/providers/gcp/services/apikeys/apikeys_service.py +++ b/prowler/providers/gcp/services/apikeys/apikeys_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/bigquery/bigquery_service.py b/prowler/providers/gcp/services/bigquery/bigquery_service.py index e06b976dc0..c61e56250e 100644 --- a/prowler/providers/gcp/services/bigquery/bigquery_service.py +++ b/prowler/providers/gcp/services/bigquery/bigquery_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/cloudresourcemanager/cloudresourcemanager_service.py b/prowler/providers/gcp/services/cloudresourcemanager/cloudresourcemanager_service.py index 28e2f4d832..7687066b95 100644 --- a/prowler/providers/gcp/services/cloudresourcemanager/cloudresourcemanager_service.py +++ b/prowler/providers/gcp/services/cloudresourcemanager/cloudresourcemanager_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/cloudsql/cloudsql_service.py b/prowler/providers/gcp/services/cloudsql/cloudsql_service.py index d0dbe5137d..1a2df51cee 100644 --- a/prowler/providers/gcp/services/cloudsql/cloudsql_service.py +++ b/prowler/providers/gcp/services/cloudsql/cloudsql_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/cloudstorage/cloudstorage_service.py b/prowler/providers/gcp/services/cloudstorage/cloudstorage_service.py index 317d30217b..65e13370fd 100644 --- a/prowler/providers/gcp/services/cloudstorage/cloudstorage_service.py +++ b/prowler/providers/gcp/services/cloudstorage/cloudstorage_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/compute/compute_service.py b/prowler/providers/gcp/services/compute/compute_service.py index e3e571ab0f..3eca63c7c9 100644 --- a/prowler/providers/gcp/services/compute/compute_service.py +++ b/prowler/providers/gcp/services/compute/compute_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/dataproc/dataproc_service.py b/prowler/providers/gcp/services/dataproc/dataproc_service.py index 68c05e407a..5b7825ceca 100644 --- a/prowler/providers/gcp/services/dataproc/dataproc_service.py +++ b/prowler/providers/gcp/services/dataproc/dataproc_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/dns/dns_service.py b/prowler/providers/gcp/services/dns/dns_service.py index dc01c5cf95..2e4ac74639 100644 --- a/prowler/providers/gcp/services/dns/dns_service.py +++ b/prowler/providers/gcp/services/dns/dns_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/gke/gke_service.py b/prowler/providers/gcp/services/gke/gke_service.py index a9b42ca046..7bd7b3214c 100644 --- a/prowler/providers/gcp/services/gke/gke_service.py +++ b/prowler/providers/gcp/services/gke/gke_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/iam/iam_service.py b/prowler/providers/gcp/services/iam/iam_service.py index 08e1ee4c3a..33287aa1ac 100644 --- a/prowler/providers/gcp/services/iam/iam_service.py +++ b/prowler/providers/gcp/services/iam/iam_service.py @@ -1,6 +1,6 @@ from datetime import datetime -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/kms/kms_service.py b/prowler/providers/gcp/services/kms/kms_service.py index 77a4c0c427..a4ec01f292 100644 --- a/prowler/providers/gcp/services/kms/kms_service.py +++ b/prowler/providers/gcp/services/kms/kms_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/logging/logging_service.py b/prowler/providers/gcp/services/logging/logging_service.py index 98f0fd1fba..e4e7d4c866 100644 --- a/prowler/providers/gcp/services/logging/logging_service.py +++ b/prowler/providers/gcp/services/logging/logging_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/monitoring/monitoring_service.py b/prowler/providers/gcp/services/monitoring/monitoring_service.py index 575434a441..d6da6caa4d 100644 --- a/prowler/providers/gcp/services/monitoring/monitoring_service.py +++ b/prowler/providers/gcp/services/monitoring/monitoring_service.py @@ -1,6 +1,6 @@ import datetime -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/gcp/services/serviceusage/serviceusage_service.py b/prowler/providers/gcp/services/serviceusage/serviceusage_service.py index f4e8ab8732..2f65da32f5 100644 --- a/prowler/providers/gcp/services/serviceusage/serviceusage_service.py +++ b/prowler/providers/gcp/services/serviceusage/serviceusage_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.gcp.gcp_provider import GcpProvider diff --git a/prowler/providers/github/models.py b/prowler/providers/github/models.py index a4fb2fdc36..b133dc9a69 100644 --- a/prowler/providers/github/models.py +++ b/prowler/providers/github/models.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.config.config import output_file_timestamp from prowler.providers.common.models import ProviderOutputOptions diff --git a/prowler/providers/github/services/organization/organization_service.py b/prowler/providers/github/services/organization/organization_service.py index 3f8f8dca95..51654fc43f 100644 --- a/prowler/providers/github/services/organization/organization_service.py +++ b/prowler/providers/github/services/organization/organization_service.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.github.lib.service.service import GithubService diff --git a/prowler/providers/github/services/repository/repository_service.py b/prowler/providers/github/services/repository/repository_service.py index 6c0101503b..dee6f35900 100644 --- a/prowler/providers/github/services/repository/repository_service.py +++ b/prowler/providers/github/services/repository/repository_service.py @@ -1,7 +1,7 @@ from datetime import datetime from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.github.lib.service.service import GithubService diff --git a/prowler/providers/kubernetes/services/core/core_service.py b/prowler/providers/kubernetes/services/core/core_service.py index 98138d0d79..e685140a87 100644 --- a/prowler/providers/kubernetes/services/core/core_service.py +++ b/prowler/providers/kubernetes/services/core/core_service.py @@ -1,7 +1,7 @@ import socket from typing import List, Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from kubernetes import client from prowler.lib.logger import logger diff --git a/prowler/providers/kubernetes/services/rbac/rbac_service.py b/prowler/providers/kubernetes/services/rbac/rbac_service.py index 3bc5d2beec..5b5fa2afc6 100644 --- a/prowler/providers/kubernetes/services/rbac/rbac_service.py +++ b/prowler/providers/kubernetes/services/rbac/rbac_service.py @@ -1,6 +1,6 @@ from typing import Any, List, Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from kubernetes import client from prowler.lib.logger import logger diff --git a/prowler/providers/m365/models.py b/prowler/providers/m365/models.py index ca9d74c44d..6f9dc0e8c8 100644 --- a/prowler/providers/m365/models.py +++ b/prowler/providers/m365/models.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.config.config import output_file_timestamp from prowler.providers.common.models import ProviderOutputOptions diff --git a/prowler/providers/m365/services/admincenter/admincenter_service.py b/prowler/providers/m365/services/admincenter/admincenter_service.py index feac9c8375..2d9db75ddf 100644 --- a/prowler/providers/m365/services/admincenter/admincenter_service.py +++ b/prowler/providers/m365/services/admincenter/admincenter_service.py @@ -1,7 +1,7 @@ from asyncio import gather, get_event_loop from typing import List, Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.m365.lib.service.service import M365Service diff --git a/prowler/providers/m365/services/defender/defender_service.py b/prowler/providers/m365/services/defender/defender_service.py index 9ea4a64afd..433d5b5422 100644 --- a/prowler/providers/m365/services/defender/defender_service.py +++ b/prowler/providers/m365/services/defender/defender_service.py @@ -1,6 +1,6 @@ from typing import List, Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.m365.lib.service.service import M365Service diff --git a/prowler/providers/m365/services/entra/entra_service.py b/prowler/providers/m365/services/entra/entra_service.py index b14f82b03c..cea86ebfe7 100644 --- a/prowler/providers/m365/services/entra/entra_service.py +++ b/prowler/providers/m365/services/entra/entra_service.py @@ -4,7 +4,7 @@ from enum import Enum from typing import List, Optional from uuid import UUID -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.m365.lib.service.service import M365Service diff --git a/prowler/providers/m365/services/exchange/exchange_service.py b/prowler/providers/m365/services/exchange/exchange_service.py index 40dc8c3893..d8b0fbfa3a 100644 --- a/prowler/providers/m365/services/exchange/exchange_service.py +++ b/prowler/providers/m365/services/exchange/exchange_service.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Optional -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.m365.lib.service.service import M365Service diff --git a/prowler/providers/m365/services/purview/purview_service.py b/prowler/providers/m365/services/purview/purview_service.py index 138305c899..c950b9e5cc 100644 --- a/prowler/providers/m365/services/purview/purview_service.py +++ b/prowler/providers/m365/services/purview/purview_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.m365.lib.service.service import M365Service diff --git a/prowler/providers/m365/services/sharepoint/sharepoint_service.py b/prowler/providers/m365/services/sharepoint/sharepoint_service.py index 244f27521f..b47176d396 100644 --- a/prowler/providers/m365/services/sharepoint/sharepoint_service.py +++ b/prowler/providers/m365/services/sharepoint/sharepoint_service.py @@ -3,7 +3,7 @@ from asyncio import gather, get_event_loop from typing import List, Optional from msgraph.generated.models.o_data_errors.o_data_error import ODataError -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.m365.lib.service.service import M365Service diff --git a/prowler/providers/m365/services/teams/teams_service.py b/prowler/providers/m365/services/teams/teams_service.py index f17f22d327..66970b0ec7 100644 --- a/prowler/providers/m365/services/teams/teams_service.py +++ b/prowler/providers/m365/services/teams/teams_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.m365.lib.service.service import M365Service diff --git a/prowler/providers/nhn/models.py b/prowler/providers/nhn/models.py index 0288e1be5d..53176524ac 100644 --- a/prowler/providers/nhn/models.py +++ b/prowler/providers/nhn/models.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.config.config import output_file_timestamp from prowler.providers.common.models import ProviderOutputOptions diff --git a/prowler/providers/nhn/services/compute/compute_service.py b/prowler/providers/nhn/services/compute/compute_service.py index 082c04bc96..efc42f8fcb 100644 --- a/prowler/providers/nhn/services/compute/compute_service.py +++ b/prowler/providers/nhn/services/compute/compute_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.nhn.nhn_provider import NhnProvider diff --git a/prowler/providers/nhn/services/network/network_service.py b/prowler/providers/nhn/services/network/network_service.py index 140e1d2815..26a805e3df 100644 --- a/prowler/providers/nhn/services/network/network_service.py +++ b/prowler/providers/nhn/services/network/network_service.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel from prowler.lib.logger import logger from prowler.providers.nhn.nhn_provider import NhnProvider diff --git a/tests/providers/gcp/services/iam/iam_service_account_unused/iam_service_account_unused_test.py b/tests/providers/gcp/services/iam/iam_service_account_unused/iam_service_account_unused_test.py index a50e1f80e4..d76200734d 100644 --- a/tests/providers/gcp/services/iam/iam_service_account_unused/iam_service_account_unused_test.py +++ b/tests/providers/gcp/services/iam/iam_service_account_unused/iam_service_account_unused_test.py @@ -96,7 +96,7 @@ class Test_iam_service_account_unused: assert result[0].resource_id == iam_client.service_accounts[0].email assert result[0].project_id == GCP_PROJECT_ID assert result[0].location == GCP_US_CENTER1_LOCATION - assert result[0].resource == iam_client.service_accounts[0].model_dump() + assert result[0].resource == iam_client.service_accounts[0] def test_iam_service_account_unused_mix(self): iam_client = mock.MagicMock() @@ -168,7 +168,7 @@ class Test_iam_service_account_unused: assert result[0].resource_id == iam_client.service_accounts[0].email assert result[0].project_id == GCP_PROJECT_ID assert result[0].location == GCP_US_CENTER1_LOCATION - assert result[0].resource == iam_client.service_accounts[0].model_dump() + assert result[0].resource == iam_client.service_accounts[0] assert result[1].status == "FAIL" assert ( @@ -178,4 +178,4 @@ class Test_iam_service_account_unused: assert result[1].resource_id == iam_client.service_accounts[1].email assert result[1].project_id == GCP_PROJECT_ID assert result[1].location == GCP_US_CENTER1_LOCATION - assert result[1].resource == iam_client.service_accounts[1].model_dump() + assert result[1].resource == iam_client.service_accounts[1] From d79ee1c5f68a810900dc4ff77a0a77be7e707085 Mon Sep 17 00:00:00 2001 From: pedrooot Date: Thu, 29 May 2025 10:57:59 +0200 Subject: [PATCH 19/24] feat(compliance): update profile validation from CIS --- .../kubernetes/cis_1.10_kubernetes.json | 30 +-- .../kubernetes/cis_1.11_kubernetes.json | 32 +-- .../kubernetes/cis_1.8_kubernetes.json | 32 +-- prowler/lib/check/compliance_models.py | 4 + prowler/providers/m365/m365_provider.py | 1 - tests/lib/check/compliance_check_test.py | 186 +++++++++++++++++- 6 files changed, 236 insertions(+), 49 deletions(-) diff --git a/prowler/compliance/kubernetes/cis_1.10_kubernetes.json b/prowler/compliance/kubernetes/cis_1.10_kubernetes.json index f1bdbd9437..449de51208 100644 --- a/prowler/compliance/kubernetes/cis_1.10_kubernetes.json +++ b/prowler/compliance/kubernetes/cis_1.10_kubernetes.json @@ -708,7 +708,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Automated", "Description": "Automate service accounts management.", "RationaleStatement": "When you create a pod, if you do not specify a service account, it is automatically assigned the `default` service account in the same namespace. You should create your own service account and let the API server manage its security tokens.", @@ -731,7 +731,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Automated", "Description": "Reject creating objects in a namespace that is undergoing termination.", "RationaleStatement": "Setting admission control policy to `NamespaceLifecycle` ensures that objects cannot be created in non-existent namespaces, and that namespaces undergoing termination are not used for creating the new objects. This is recommended to enforce the integrity of the namespace termination process and also for the availability of the newer objects.", @@ -754,7 +754,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Automated", "Description": "Limit the `Node` and `Pod` objects that a kubelet could modify.", "RationaleStatement": "Using the `NodeRestriction` plug-in ensures that the kubelet is restricted to the `Node` and `Pod` objects that it could modify as defined. Such kubelets will only be allowed to modify their own `Node` API object, and only modify `Pod` API objects that are bound to their node.", @@ -1458,7 +1458,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Use a different certificate authority for etcd from the one used for Kubernetes.", "RationaleStatement": "etcd is a highly available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. Its access should be restricted to specifically designated clients and peers only. Authentication to etcd is based on whether the certificate presented was issued by a trusted certificate authority. There is no checking of certificate attributes such as common name or subject alternative name. As such, if any attackers were able to gain access to any certificate issued by the trusted certificate authority, they would be able to gain full access to the etcd database.", @@ -1563,7 +1563,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.2 Logging", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Ensure that the audit policy created for the cluster covers key security concerns.", "RationaleStatement": "Security audit logs should cover access and modification of key resources in the cluster, to enable them to form an effective part of a security environment.", @@ -1967,7 +1967,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 2 - Worker Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Security relevant information should be captured. The eventRecordQPS on the Kubelet configuration can be used to limit the rate at which events are gathered and sets the maximum event creations per second. Setting this too low could result in relevant events not being logged, however the unlimited setting of `0` could result in a denial of service on the kubelet.", "RationaleStatement": "It is important to capture all events and not restrict event creation. Events are an important source of security information and analytics that ensure that your environment is consistently monitored using the event data.", @@ -2549,7 +2549,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run as the root user.", "RationaleStatement": "Containers may run as any Linux user. Containers which run as the root user, whilst constrained by Container Runtime security features still have a escalated likelihood of container breakout.Ideally, all containers should run as a defined non-UID 0 user.There should be at least one admission control policy defined which does not permit root containers.If you need to run root containers, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2618,7 +2618,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Ensure that the cluster has at least one active policy control mechanism in place", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers with capabilities", "RationaleStatement": "Containers run with a default set of capabilities as assigned by the Container Runtime. Capabilities are parts of the rights generally granted on a Linux system to the root user.In many cases applications running in containers do not require any capabilities to operate, so from the perspective of the principal of least privilege use of capabilities should be minimized.", @@ -2727,7 +2727,7 @@ { "Section": "5 Policies", "SubSection": "5.3 Network Policies and CNI", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Use network policies to isolate traffic in your cluster network.", "RationaleStatement": "Running different applications on the same Kubernetes cluster creates a risk of one compromised application attacking a neighboring application. Network segmentation is important to ensure that containers can communicate only with those they are supposed to. A network policy is a specification of how selections of pods are allowed to communicate with each other and other network endpoints. Network Policies are namespace scoped. When a network policy is introduced to a given namespace, all traffic not allowed by the policy is denied. However, if there are no network policies in a namespace all traffic will be allowed into and out of the pods in that namespace.", @@ -2750,7 +2750,7 @@ { "Section": "5 Policies", "SubSection": "5.4 Secrets Management", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Kubernetes supports mounting secrets as data volumes or as environment variables. Minimize the use of environment variable secrets.", "RationaleStatement": "It is reasonably common for application code to log out its environment (particularly in the event of an error). This will include any secret values passed in as environment variables, so secrets can easily be exposed to any user or entity who has access to the logs.", @@ -2771,7 +2771,7 @@ { "Section": "5 Policies", "SubSection": "5.4 Secrets Management", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Consider the use of an external secrets storage and management system, instead of using Kubernetes Secrets directly, if you have more complex secret management needs. Ensure the solution requires authentication to access secrets, has auditing of access to and use of secrets, and encrypts secrets. Some solutions also make it easier to rotate secrets.", "RationaleStatement": "Kubernetes supports secrets as first-class objects, but care needs to be taken to ensure that access to secrets is carefully limited. Using an external secrets provider can ease the management of access to secrets, especially where secrests are used across both Kubernetes and non-Kubernetes environments.", @@ -2792,7 +2792,7 @@ { "Section": "5 Policies", "SubSection": "5.5 Extensible Admission Control", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Configure Image Provenance for your deployment.", "RationaleStatement": "Kubernetes supports plugging in provenance rules to accept or reject the images in your deployments. You could configure such rules to ensure that only approved images are deployed in the cluster.", @@ -2836,7 +2836,7 @@ { "Section": "5 Policies", "SubSection": "5.7 General Policies", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Enable `docker/default` seccomp profile in your pod definitions.", "RationaleStatement": "Seccomp (secure computing mode) is used to restrict the set of system calls applications can make, allowing cluster administrators greater control over the security of workloads running in the cluster. Kubernetes disables seccomp profiles by default for historical reasons. You should enable it to ensure that the workloads have restricted actions available within the container.", @@ -2857,7 +2857,7 @@ { "Section": "5 Policies", "SubSection": "5.7 General Policies", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Apply Security Context to Your Pods and Containers", "RationaleStatement": "A security context defines the operating system security settings (uid, gid, capabilities, SELinux role, etc..) applied to a container. When designing your containers and pods, make sure that you configure the security context for your pods, containers, and volumes. A security context is a property defined in the deployment yaml. It controls the security parameters that will be assigned to the pod/container/volume. There are two levels of security context: pod level security context, and container level security context.", @@ -2878,7 +2878,7 @@ { "Section": "5 Policies", "SubSection": "5.7 General Policies", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Kubernetes provides a default namespace, where objects are placed if no namespace is specified for them. Placing objects in this namespace makes application of RBAC and other controls more difficult.", "RationaleStatement": "Resources in a Kubernetes cluster should be segregated by namespace, to allow for security controls to be applied at that level and to make it easier to manage resources.", diff --git a/prowler/compliance/kubernetes/cis_1.11_kubernetes.json b/prowler/compliance/kubernetes/cis_1.11_kubernetes.json index 0faaa94196..832533624e 100644 --- a/prowler/compliance/kubernetes/cis_1.11_kubernetes.json +++ b/prowler/compliance/kubernetes/cis_1.11_kubernetes.json @@ -708,7 +708,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Automated", "Description": "Automate service accounts management.", "RationaleStatement": "When you create a pod, if you do not specify a service account, it is automatically assigned the `default` service account in the same namespace. You should create your own service account and let the API server manage its security tokens.", @@ -731,7 +731,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Automated", "Description": "Reject creating objects in a namespace that is undergoing termination.", "RationaleStatement": "Setting admission control policy to `NamespaceLifecycle` ensures that objects cannot be created in non-existent namespaces, and that namespaces undergoing termination are not used for creating the new objects. This is recommended to enforce the integrity of the namespace termination process and also for the availability of the newer objects.", @@ -754,7 +754,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Automated", "Description": "Limit the `Node` and `Pod` objects that a kubelet could modify.", "RationaleStatement": "Using the `NodeRestriction` plug-in ensures that the kubelet is restricted to the `Node` and `Pod` objects that it could modify as defined. Such kubelets will only be allowed to modify their own `Node` API object, and only modify `Pod` API objects that are bound to their node.", @@ -1501,7 +1501,7 @@ "Attributes": [ { "Section": "2 etcd", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Use a different certificate authority for etcd from the one used for Kubernetes.", "RationaleStatement": "etcd is a highly available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. Its access should be restricted to specifically designated clients and peers only. Authentication to etcd is based on whether the certificate presented was issued by a trusted certificate authority. There is no checking of certificate attributes such as common name or subject alternative name. As such, if any attackers were able to gain access to any certificate issued by the trusted certificate authority, they would be able to gain full access to the etcd database.", @@ -1606,7 +1606,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.2 Logging", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Ensure that the audit policy created for the cluster covers key security concerns.", "RationaleStatement": "Security audit logs should cover access and modification of key resources in the cluster, to enable them to form an effective part of a security environment.", @@ -2010,7 +2010,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 2 - Worker Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Security relevant information should be captured. The eventRecordQPS on the Kubelet configuration can be used to limit the rate at which events are gathered and sets the maximum event creations per second. Setting this too low could result in relevant events not being logged, however the unlimited setting of `0` could result in a denial of service on the kubelet.", "RationaleStatement": "It is important to capture all events and not restrict event creation. Events are an important source of security information and analytics that ensure that your environment is consistently monitored using the event data.", @@ -2163,7 +2163,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 2 - Worker Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Ensuring that `--IPAddressDeny` is set to Any will facilitate allowlisting of only IP addresses that are explicitly set with the `--IPAddressAllow` parameter which will block unspecified IP addresses from communicating with the **kubelet** component.", "RationaleStatement": "By default, Kubernetes allows any IP address to communicate with the **kubelet** component IP restrictions and IP whitelisting are security best practices and reduce the attack surface of the **kubelet**.", @@ -2634,7 +2634,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers to be run as the root user.", "RationaleStatement": "Containers may run as any Linux user. Containers which run as the root user, whilst constrained by Container Runtime security features still have a escalated likelihood of container breakout. Ideally, all containers should run as a defined non-UID 0 user. There should be at least one admission control policy defined which does not permit root containers. If you need to run root containers, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2703,7 +2703,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers with capabilities", "RationaleStatement": "Containers run with a default set of capabilities as assigned by the Container Runtime. Capabilities are parts of the rights generally granted on a Linux system to the root user. In many cases applications running in containers do not require any capabilities to operate, so from the perspective of the principal of least privilege use of capabilities should be minimized.", @@ -2812,7 +2812,7 @@ { "Section": "5 Policies", "SubSection": "5.3 Network Policies and CNI", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Use network policies to isolate traffic in your cluster network.", "RationaleStatement": "Running different applications on the same Kubernetes cluster creates a risk of one compromised application attacking a neighboring application. Network segmentation is important to ensure that containers can communicate only with those they are supposed to. A network policy is a specification of how selections of pods are allowed to communicate with each other and other network endpoints. Network Policies are namespace scoped. When a network policy is introduced to a given namespace, all traffic not allowed by the policy is denied. However, if there are no network policies in a namespace all traffic will be allowed into and out of the pods in that namespace.", @@ -2835,7 +2835,7 @@ { "Section": "5 Policies", "SubSection": "5.4 Secrets Management", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Kubernetes supports mounting secrets as data volumes or as environment variables. Minimize the use of environment variable secrets.", "RationaleStatement": "It is reasonably common for application code to log out its environment (particularly in the event of an error). This will include any secret values passed in as environment variables, so secrets can easily be exposed to any user or entity who has access to the logs.", @@ -2856,7 +2856,7 @@ { "Section": "5 Policies", "SubSection": "5.4 Secrets Management", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Consider the use of an external secrets storage and management system, instead of using Kubernetes Secrets directly, if you have more complex secret management needs. Ensure the solution requires authentication to access secrets, has auditing of access to and use of secrets, and encrypts secrets. Some solutions also make it easier to rotate secrets.", "RationaleStatement": "Kubernetes supports secrets as first-class objects, but care needs to be taken to ensure that access to secrets is carefully limited. Using an external secrets provider can ease the management of access to secrets, especially where secrests are used across both Kubernetes and non-Kubernetes environments.", @@ -2877,7 +2877,7 @@ { "Section": "5 Policies", "SubSection": "5.5 Extensible Admission Control", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Configure Image Provenance for your deployment.", "RationaleStatement": "Kubernetes supports plugging in provenance rules to accept or reject the images in your deployments. You could configure such rules to ensure that only approved images are deployed in the cluster.", @@ -2921,7 +2921,7 @@ { "Section": "5 Policies", "SubSection": "5.6 General Policies", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Enable `docker/default` seccomp profile in your pod definitions.", "RationaleStatement": "Seccomp (secure computing mode) is used to restrict the set of system calls applications can make, allowing cluster administrators greater control over the security of workloads running in the cluster. Kubernetes disables seccomp profiles by default for historical reasons. You should enable it to ensure that the workloads have restricted actions available within the container.", @@ -2942,7 +2942,7 @@ { "Section": "5 Policies", "SubSection": "5.6 General Policies", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Apply Security Context to Your Pods and Containers", "RationaleStatement": "A security context defines the operating system security settings (uid, gid, capabilities, SELinux role, etc..) applied to a container. When designing your containers and pods, make sure that you configure the security context for your pods, containers, and volumes. A security context is a property defined in the deployment yaml. It controls the security parameters that will be assigned to the pod/container/volume. There are two levels of security context: pod level security context, and container level security context.", @@ -2963,7 +2963,7 @@ { "Section": "5 Policies", "SubSection": "5.6 General Policies", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Kubernetes provides a default namespace, where objects are placed if no namespace is specified for them. Placing objects in this namespace makes application of RBAC and other controls more difficult.", "RationaleStatement": "Resources in a Kubernetes cluster should be segregated by namespace, to allow for security controls to be applied at that level and to make it easier to manage resources.", diff --git a/prowler/compliance/kubernetes/cis_1.8_kubernetes.json b/prowler/compliance/kubernetes/cis_1.8_kubernetes.json index af8d9e6c8a..280375fc31 100644 --- a/prowler/compliance/kubernetes/cis_1.8_kubernetes.json +++ b/prowler/compliance/kubernetes/cis_1.8_kubernetes.json @@ -708,7 +708,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "The SecurityContextDeny admission controller can be used to deny pods which make use of some SecurityContext fields which could allow for privilege escalation in the cluster. This should be used where PodSecurityPolicy is not in place within the cluster.", "RationaleStatement": "SecurityContextDeny can be used to provide a layer of security for clusters which do not have PodSecurityPolicies enabled.", @@ -731,7 +731,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Automated", "Description": "Automate service accounts management.", "RationaleStatement": "When you create a pod, if you do not specify a service account, it is automatically assigned the `default` service account in the same namespace. You should create your own service account and let the API server manage its security tokens.", @@ -754,7 +754,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Automated", "Description": "Reject creating objects in a namespace that is undergoing termination.", "RationaleStatement": "Setting admission control policy to `NamespaceLifecycle` ensures that objects cannot be created in non-existent namespaces, and that namespaces undergoing termination are not used for creating the new objects. This is recommended to enforce the integrity of the namespace termination process and also for the availability of the newer objects.", @@ -777,7 +777,7 @@ { "Section": "1 Control Plane Components", "SubSection": "1.2 API Server", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Automated", "Description": "Limit the `Node` and `Pod` objects that a kubelet could modify.", "RationaleStatement": "Using the `NodeRestriction` plug-in ensures that the kubelet is restricted to the `Node` and `Pod` objects that it could modify as defined. Such kubelets will only be allowed to modify their own `Node` API object, and only modify `Pod` API objects that are bound to their node.", @@ -1481,7 +1481,7 @@ "Attributes": [ { "Section": "2 Etcd", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Use a different certificate authority for etcd from the one used for Kubernetes.", "RationaleStatement": "etcd is a highly available key-value store used by Kubernetes deployments for persistent storage of all of its REST API objects. Its access should be restricted to specifically designated clients and peers only. Authentication to etcd is based on whether the certificate presented was issued by a trusted certificate authority. There is no checking of certificate attributes such as common name or subject alternative name. As such, if any attackers were able to gain access to any certificate issued by the trusted certificate authority, they would be able to gain full access to the etcd database.", @@ -1586,7 +1586,7 @@ { "Section": "3 Control Plane Configuration", "SubSection": "3.2 Logging", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Ensure that the audit policy created for the cluster covers key security concerns.", "RationaleStatement": "Security audit logs should cover access and modification of key resources in the cluster, to enable them to form an effective part of a security environment.", @@ -1990,7 +1990,7 @@ { "Section": "4 Worker Nodes", "SubSection": "4.2 Kubelet", - "Profile": "Level 2 - Worker Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Security relevant information should be captured. The eventRecordQPS on the Kubelet configuration can be used to limit the rate at which events are gathered and sets the maximum event creations per second. Setting this too low could result in relevant events not being logged, however the unlimited setting of `0` could result in a denial of service on the kubelet.", "RationaleStatement": "It is important to capture all events and not restrict event creation. Events are an important source of security information and analytics that ensure that your environment is consistently monitored using the event data.", @@ -2551,7 +2551,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Automated", "Description": "Do not generally permit containers to be run as the root user.", "RationaleStatement": "Containers may run as any Linux user. Containers which run as the root user, whilst constrained by Container Runtime security features still have a escalated likelihood of container breakout. Ideally, all containers should run as a defined non-UID 0 user. There should be at least one admission control policy defined which does not permit root containers. If you need to run root containers, this should be defined in a separate policy and you should carefully check to ensure that only limited service accounts and users are given permission to use that policy.", @@ -2620,7 +2620,7 @@ { "Section": "5 Policies", "SubSection": "5.2 Pod Security Standards", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Do not generally permit containers with capabilities", "RationaleStatement": "Containers run with a default set of capabilities as assigned by the Container Runtime. Capabilities are parts of the rights generally granted on a Linux system to the root user. In many cases applications running in containers do not require any capabilities to operate, so from the perspective of the principal of least privilege use of capabilities should be minimized.", @@ -2729,7 +2729,7 @@ { "Section": "5 Policies", "SubSection": "5.3 Network Policies and CNI", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Use network policies to isolate traffic in your cluster network.", "RationaleStatement": "Running different applications on the same Kubernetes cluster creates a risk of one compromised application attacking a neighboring application. Network segmentation is important to ensure that containers can communicate only with those they are supposed to. A network policy is a specification of how selections of pods are allowed to communicate with each other and other network endpoints. Network Policies are namespace scoped. When a network policy is introduced to a given namespace, all traffic not allowed by the policy is denied. However, if there are no network policies in a namespace all traffic will be allowed into and out of the pods in that namespace.", @@ -2752,7 +2752,7 @@ { "Section": "5 Policies", "SubSection": "5.4 Secrets Management", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Kubernetes supports mounting secrets as data volumes or as environment variables. Minimize the use of environment variable secrets.", "RationaleStatement": "It is reasonably common for application code to log out its environment (particularly in the event of an error). This will include any secret values passed in as environment variables, so secrets can easily be exposed to any user or entity who has access to the logs.", @@ -2773,7 +2773,7 @@ { "Section": "5 Policies", "SubSection": "5.4 Secrets Management", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Consider the use of an external secrets storage and management system, instead of using Kubernetes Secrets directly, if you have more complex secret management needs. Ensure the solution requires authentication to access secrets, has auditing of access to and use of secrets, and encrypts secrets. Some solutions also make it easier to rotate secrets.", "RationaleStatement": "Kubernetes supports secrets as first-class objects, but care needs to be taken to ensure that access to secrets is carefully limited. Using an external secrets provider can ease the management of access to secrets, especially where secrests are used across both Kubernetes and non-Kubernetes environments.", @@ -2794,7 +2794,7 @@ { "Section": "5 Policies", "SubSection": "5.4 Secrets Management", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Configure Image Provenance for your deployment.", "RationaleStatement": "Kubernetes supports plugging in provenance rules to accept or reject the images in your deployments. You could configure such rules to ensure that only approved images are deployed in the cluster.", @@ -2838,7 +2838,7 @@ { "Section": "5 Policies", "SubSection": "5.7 General Policies", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Enable `docker/default` seccomp profile in your pod definitions.", "RationaleStatement": "Seccomp (secure computing mode) is used to restrict the set of system calls applications can make, allowing cluster administrators greater control over the security of workloads running in the cluster. Kubernetes disables seccomp profiles by default for historical reasons. You should enable it to ensure that the workloads have restricted actions available within the container.", @@ -2859,7 +2859,7 @@ { "Section": "5 Policies", "SubSection": "5.7 General Policies", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Apply Security Context to Your Pods and Containers", "RationaleStatement": "A security context defines the operating system security settings (uid, gid, capabilities, SELinux role, etc..) applied to a container. When designing your containers and pods, make sure that you configure the security context for your pods, containers, and volumes. A security context is a property defined in the deployment yaml. It controls the security parameters that will be assigned to the pod/container/volume. There are two levels of security context: pod level security context, and container level security context.", @@ -2880,7 +2880,7 @@ { "Section": "5 Policies", "SubSection": "5.7 General Policies", - "Profile": "Level 2 - Master Node", + "Profile": "Level 2", "AssessmentStatus": "Manual", "Description": "Kubernetes provides a default namespace, where objects are placed if no namespace is specified for them. Placing objects in this namespace makes application of RBAC and other controls more difficult.", "RationaleStatement": "Resources in a Kubernetes cluster should be segregated by namespace, to allow for security controls to be applied at that level and to make it easier to manage resources.", diff --git a/prowler/lib/check/compliance_models.py b/prowler/lib/check/compliance_models.py index b6ab382101..0d0200ba63 100644 --- a/prowler/lib/check/compliance_models.py +++ b/prowler/lib/check/compliance_models.py @@ -69,6 +69,10 @@ class CIS_Requirement_Attribute_Profile(str, Enum): Level_1 = "Level 1" Level_2 = "Level 2" + E3_Level_1 = "E3 Level 1" + E3_Level_2 = "E3 Level 2" + E5_Level_1 = "E5 Level 1" + E5_Level_2 = "E5 Level 2" class CIS_Requirement_Attribute_AssessmentStatus(str, Enum): diff --git a/prowler/providers/m365/m365_provider.py b/prowler/providers/m365/m365_provider.py index cf647ce978..d64d28d22b 100644 --- a/prowler/providers/m365/m365_provider.py +++ b/prowler/providers/m365/m365_provider.py @@ -350,7 +350,6 @@ class M365Provider(Provider): """ try: config = get_regions_config(region) - return M365RegionConfig( name=region, authority=config["authority"], diff --git a/tests/lib/check/compliance_check_test.py b/tests/lib/check/compliance_check_test.py index 6994d00c29..b6789b3832 100644 --- a/tests/lib/check/compliance_check_test.py +++ b/tests/lib/check/compliance_check_test.py @@ -94,6 +94,93 @@ custom_compliance_metadata = { ) ], ), + "framework1_gcp": Compliance( + Framework="Framework1", + Provider="gcp", + Version="1.0", + Description="Framework 2 Description", + Requirements=[ + Compliance_Requirement( + Id="1.1.1", + Description="description", + Attributes=[ + CIS_Requirement_Attribute( + Section="1. Identity", + Profile=CIS_Requirement_Attribute_Profile("Level 1"), + AssessmentStatus=CIS_Requirement_Attribute_AssessmentStatus( + "Manual" + ), + Description="Description", + RationaleStatement="Rationale", + ImpactStatement="Impact", + RemediationProcedure="Remediation", + AuditProcedure="Audit", + AdditionalInformation="Additional", + References="References", + ) + ], + Checks=[], + ) + ], + ), + "framework1_k8s": Compliance( + Framework="Framework1", + Provider="Kubernetes", + Version="1.0", + Description="Framework 2 Description", + Requirements=[ + Compliance_Requirement( + Id="1.1.1", + Description="description", + Attributes=[ + CIS_Requirement_Attribute( + Section="1. Identity", + Profile=CIS_Requirement_Attribute_Profile("Level 1"), + AssessmentStatus=CIS_Requirement_Attribute_AssessmentStatus( + "Manual" + ), + Description="Description", + RationaleStatement="Rationale", + ImpactStatement="Impact", + RemediationProcedure="Remediation", + AuditProcedure="Audit", + AdditionalInformation="Additional", + References="References", + ) + ], + Checks=[], + ) + ], + ), + "framework1_m365": Compliance( + Framework="Framework1", + Provider="m365", + Version="1.0", + Description="Framework 2 Description", + Requirements=[ + Compliance_Requirement( + Id="1.1.1", + Description="description", + Attributes=[ + CIS_Requirement_Attribute( + Section="1. Identity", + Profile=CIS_Requirement_Attribute_Profile("E3 Level 1"), + AssessmentStatus=CIS_Requirement_Attribute_AssessmentStatus( + "Manual" + ), + Description="Description", + RationaleStatement="Rationale", + ImpactStatement="Impact", + RemediationProcedure="Remediation", + AuditProcedure="Audit", + AdditionalInformation="Additional", + References="References", + ) + ], + Checks=[], + ) + ], + ), } @@ -209,9 +296,12 @@ class TestCompliance: list_compliance = Compliance.list(bulk_compliance_frameworks) - assert len(list_compliance) == 2 + assert len(list_compliance) == 5 assert list_compliance[0] == "framework1_aws" assert list_compliance[1] == "framework1_azure" + assert list_compliance[2] == "framework1_gcp" + assert list_compliance[3] == "framework1_k8s" + assert list_compliance[4] == "framework1_m365" def test_list_with_provider_aws(self): bulk_compliance_frameworks = custom_compliance_metadata @@ -229,6 +319,30 @@ class TestCompliance: assert len(list_compliance) == 1 assert list_compliance[0] == "framework1_azure" + def test_list_with_provider_gcp(self): + bulk_compliance_frameworks = custom_compliance_metadata + + list_compliance = Compliance.list(bulk_compliance_frameworks, provider="gcp") + + assert len(list_compliance) == 1 + assert list_compliance[0] == "framework1_gcp" + + def test_list_with_provider_k8s(self): + bulk_compliance_frameworks = custom_compliance_metadata + + list_compliance = Compliance.list(bulk_compliance_frameworks, provider="k8s") + + assert len(list_compliance) == 1 + assert list_compliance[0] == "framework1_k8s" + + def test_list_with_provider_m365(self): + bulk_compliance_frameworks = custom_compliance_metadata + + list_compliance = Compliance.list(bulk_compliance_frameworks, provider="m365") + + assert len(list_compliance) == 1 + assert list_compliance[0] == "framework1_m365" + def test_get_compliance_frameworks(self): bulk_compliance_frameworks = custom_compliance_metadata @@ -252,6 +366,76 @@ class TestCompliance: assert compliance_framework.Description == "Framework 2 Description" assert len(compliance_framework.Requirements) == 1 + compliance_framework = Compliance.get( + bulk_compliance_frameworks, compliance_framework_name="framework1_gcp" + ) + + assert compliance_framework.Framework == "Framework1" + assert compliance_framework.Provider == "gcp" + assert compliance_framework.Version == "1.0" + assert compliance_framework.Description == "Framework 2 Description" + assert len(compliance_framework.Requirements) == 1 + + compliance_framework = Compliance.get( + bulk_compliance_frameworks, compliance_framework_name="framework1_k8s" + ) + + assert compliance_framework.Framework == "Framework1" + assert compliance_framework.Provider == "Kubernetes" + assert compliance_framework.Version == "1.0" + assert compliance_framework.Description == "Framework 2 Description" + assert len(compliance_framework.Requirements) == 1 + + compliance_framework = Compliance.get( + bulk_compliance_frameworks, compliance_framework_name="framework1_m365" + ) + + assert compliance_framework.Framework == "Framework1" + assert compliance_framework.Provider == "m365" + assert compliance_framework.Version == "1.0" + assert compliance_framework.Description == "Framework 2 Description" + assert len(compliance_framework.Requirements) == 1 + assert compliance_framework.Requirements[0].Id == "1.1.1" + assert compliance_framework.Requirements[0].Description == "description" + assert len(compliance_framework.Requirements[0].Attributes) == 1 + assert ( + compliance_framework.Requirements[0].Attributes[0].Section == "1. Identity" + ) + assert ( + compliance_framework.Requirements[0].Attributes[0].Profile == "E3 Level 1" + ) + assert ( + compliance_framework.Requirements[0].Attributes[0].AssessmentStatus + == "Manual" + ) + assert ( + compliance_framework.Requirements[0].Attributes[0].Description + == "Description" + ) + assert ( + compliance_framework.Requirements[0].Attributes[0].RationaleStatement + == "Rationale" + ) + assert ( + compliance_framework.Requirements[0].Attributes[0].ImpactStatement + == "Impact" + ) + assert ( + compliance_framework.Requirements[0].Attributes[0].RemediationProcedure + == "Remediation" + ) + assert ( + compliance_framework.Requirements[0].Attributes[0].AuditProcedure == "Audit" + ) + assert ( + compliance_framework.Requirements[0].Attributes[0].AdditionalInformation + == "Additional" + ) + assert ( + compliance_framework.Requirements[0].Attributes[0].References + == "References" + ) + def test_get_non_existent_framework(self): bulk_compliance_frameworks = custom_compliance_metadata From 7ec9f6572f8f5c8d703a21bbc4ab662874e7ee1d Mon Sep 17 00:00:00 2001 From: Andoni Alonso <14891798+andoniaf@users.noreply.github.com> Date: Thu, 29 May 2025 12:39:58 +0200 Subject: [PATCH 20/24] chore(iac): add iac provider tests (#7874) --- prowler/providers/iac/iac_provider.py | 1 - tests/providers/iac/iac_fixtures.py | 67 ++++++++++++ tests/providers/iac/iac_provider_test.py | 129 +++++++++++++++++++++++ 3 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 tests/providers/iac/iac_fixtures.py create mode 100644 tests/providers/iac/iac_provider_test.py diff --git a/prowler/providers/iac/iac_provider.py b/prowler/providers/iac/iac_provider.py index 26b54a5b3d..f84f9250fb 100644 --- a/prowler/providers/iac/iac_provider.py +++ b/prowler/providers/iac/iac_provider.py @@ -98,7 +98,6 @@ class IacProvider(Provider): Returns: CheckReportIAC: The processed check report """ - metadata_dict = { "Provider": "iac", "CheckID": check.get("check_id", ""), diff --git a/tests/providers/iac/iac_fixtures.py b/tests/providers/iac/iac_fixtures.py new file mode 100644 index 0000000000..70f0698bdf --- /dev/null +++ b/tests/providers/iac/iac_fixtures.py @@ -0,0 +1,67 @@ +# IAC Provider Constants +DEFAULT_SCAN_PATH = "." + +# Sample Checkov Output +SAMPLE_CHECKOV_OUTPUT = [ + { + "check_type": "terraform", + "results": { + "failed_checks": [ + { + "check_id": "CKV_AWS_1", + "check_name": "Ensure S3 bucket has encryption enabled", + "guideline": "https://docs.bridgecrew.io/docs/s3_1-s3-bucket-has-encryption-enabled", + "severity": "low", + }, + { + "check_id": "CKV_AWS_2", + "check_name": "Ensure S3 bucket has public access blocked", + "guideline": "https://docs.bridgecrew.io/docs/s3_2-s3-bucket-has-public-access-blocked", + "severity": "low", + }, + ], + "passed_checks": [ + { + "check_id": "CKV_AWS_3", + "check_name": "Ensure S3 bucket has versioning enabled", + "guideline": "https://docs.bridgecrew.io/docs/s3_3-s3-bucket-has-versioning-enabled", + "severity": "low", + } + ], + }, + } +] + +# Sample Finding Data +SAMPLE_FINDING = SAMPLE_CHECKOV_OUTPUT[0] + +SAMPLE_FAILED_CHECK = { + "check_id": "CKV_AWS_1", + "check_name": "Ensure S3 bucket has encryption enabled", + "guideline": "https://docs.bridgecrew.io/docs/s3_1-s3-bucket-has-encryption-enabled", + "severity": "low", +} + +SAMPLE_PASSED_CHECK = { + "check_id": "CKV_AWS_3", + "check_name": "Ensure S3 bucket has versioning enabled", + "guideline": "https://docs.bridgecrew.io/docs/s3_3-s3-bucket-has-versioning-enabled", + "severity": "low", +} + + +def get_sample_checkov_json_output(): + """Return sample Checkov JSON output as string""" + import json + + return json.dumps(SAMPLE_CHECKOV_OUTPUT) + + +def get_empty_checkov_output(): + """Return empty Checkov output as string""" + return "[]" + + +def get_invalid_checkov_output(): + """Return invalid JSON output as string""" + return "invalid json output" diff --git a/tests/providers/iac/iac_provider_test.py b/tests/providers/iac/iac_provider_test.py new file mode 100644 index 0000000000..7d27938abd --- /dev/null +++ b/tests/providers/iac/iac_provider_test.py @@ -0,0 +1,129 @@ +from unittest.mock import MagicMock, patch + +from prowler.lib.check.models import CheckReportIAC +from prowler.providers.iac.iac_provider import IacProvider +from tests.providers.iac.iac_fixtures import ( + DEFAULT_SCAN_PATH, + SAMPLE_FAILED_CHECK, + SAMPLE_FINDING, + SAMPLE_PASSED_CHECK, + get_empty_checkov_output, + get_invalid_checkov_output, + get_sample_checkov_json_output, +) + + +class TestIacProvider: + def test_iac_provider(self): + """Test IAC provider with default parameters""" + + provider = IacProvider() + + assert provider._type == "iac" + assert provider.type == "iac" + assert provider.scan_path == DEFAULT_SCAN_PATH + assert provider._audit_config == {} + assert provider._mutelist is None + + def test_iac_provider_custom_scan_path(self): + """Test IAC provider with custom scan path""" + custom_path = "/custom/path" + + provider = IacProvider(scan_path=custom_path) + + assert provider._type == "iac" + assert provider.scan_path == custom_path + + def test_iac_provider_process_check_failed(self): + """Test processing a failed check""" + provider = IacProvider() + + report = provider._process_check(SAMPLE_FINDING, SAMPLE_FAILED_CHECK, "FAIL") + + assert isinstance(report, CheckReportIAC) + assert report.status == "FAIL" + + assert report.check_metadata.Provider == "iac" + assert report.check_metadata.CheckID == SAMPLE_FAILED_CHECK["check_id"] + assert report.check_metadata.CheckTitle == SAMPLE_FAILED_CHECK["check_name"] + assert report.check_metadata.Severity == "low" + assert report.check_metadata.RelatedUrl == SAMPLE_FAILED_CHECK["guideline"] + + def test_iac_provider_process_check_passed(self): + """Test processing a passed check""" + provider = IacProvider() + + report = provider._process_check(SAMPLE_FINDING, SAMPLE_PASSED_CHECK, "PASS") + + assert isinstance(report, CheckReportIAC) + assert report.status == "PASS" + + assert report.check_metadata.Provider == "iac" + assert report.check_metadata.CheckID == SAMPLE_PASSED_CHECK["check_id"] + assert report.check_metadata.CheckTitle == SAMPLE_PASSED_CHECK["check_name"] + assert report.check_metadata.Severity == "low" + + @patch("subprocess.run") + def test_iac_provider_run_scan_success(self, mock_subprocess): + """Test successful IAC scan with Checkov""" + provider = IacProvider() + + mock_subprocess.return_value = MagicMock( + stdout=get_sample_checkov_json_output(), stderr="" + ) + + reports = provider.run_scan("/test/directory") + + # Should have 2 failed checks + 1 passed check = 3 total reports + assert len(reports) == 3 + + # Check that we have both failed and passed reports + failed_reports = [r for r in reports if r.status == "FAIL"] + passed_reports = [r for r in reports if r.status == "PASS"] + + assert len(failed_reports) == 2 + assert len(passed_reports) == 1 + + # Verify subprocess was called correctly + mock_subprocess.assert_called_once_with( + ["checkov", "-d", "/test/directory", "-o", "json"], + capture_output=True, + text=True, + ) + + @patch("subprocess.run") + def test_iac_provider_run_scan_empty_output(self, mock_subprocess): + """Test IAC scan with empty Checkov output""" + provider = IacProvider() + + mock_subprocess.return_value = MagicMock( + stdout=get_empty_checkov_output(), stderr="" + ) + + reports = provider.run_scan("/test/directory") + + assert len(reports) == 0 + + @patch("subprocess.run") + def test_iac_provider_run_scan_invalid_json(self, mock_subprocess): + """Test IAC scan with invalid JSON output""" + provider = IacProvider() + + mock_subprocess.return_value = MagicMock( + stdout=get_invalid_checkov_output(), stderr="" + ) + + reports = provider.run_scan("/test/directory") + + assert len(reports) == 0 + + @patch("subprocess.run") + def test_iac_provider_run_scan_null_output(self, mock_subprocess): + """Test IAC scan with null Checkov output""" + provider = IacProvider() + + mock_subprocess.return_value = MagicMock(stdout="null", stderr="") + + reports = provider.run_scan("/test/directory") + + assert len(reports) == 0 From 0928f47b359f728c62e4ec99d4010271ecc991c8 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Thu, 29 May 2025 12:49:58 +0200 Subject: [PATCH 21/24] chore: improve checkov error --- prowler/providers/iac/iac_provider.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/prowler/providers/iac/iac_provider.py b/prowler/providers/iac/iac_provider.py index f84f9250fb..c22a4daa5c 100644 --- a/prowler/providers/iac/iac_provider.py +++ b/prowler/providers/iac/iac_provider.py @@ -162,9 +162,7 @@ class IacProvider(Provider): # Log Checkov's error output if any if process.stderr: - logger.critical( - f"{process.stderr.__class__.__name__} -- {process.stderr}" - ) + logger.critical(process.stderr) sys.exit(1) try: From 469bc13e5f53eaaecab31990a978f3f3c9d66ac3 Mon Sep 17 00:00:00 2001 From: MrCloudSec Date: Thu, 29 May 2025 14:28:30 +0200 Subject: [PATCH 22/24] fix: iac test --- tests/providers/iac/iac_provider_test.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/providers/iac/iac_provider_test.py b/tests/providers/iac/iac_provider_test.py index 7d27938abd..e2f3469569 100644 --- a/tests/providers/iac/iac_provider_test.py +++ b/tests/providers/iac/iac_provider_test.py @@ -1,5 +1,7 @@ from unittest.mock import MagicMock, patch +import pytest + from prowler.lib.check.models import CheckReportIAC from prowler.providers.iac.iac_provider import IacProvider from tests.providers.iac.iac_fixtures import ( @@ -113,9 +115,10 @@ class TestIacProvider: stdout=get_invalid_checkov_output(), stderr="" ) - reports = provider.run_scan("/test/directory") + with pytest.raises(SystemExit) as excinfo: + provider.run_scan("/test/directory") - assert len(reports) == 0 + assert excinfo.value.code == 1 @patch("subprocess.run") def test_iac_provider_run_scan_null_output(self, mock_subprocess): From d24f395f445210f1628dcc14b833dd3274065450 Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Thu, 29 May 2025 17:58:34 +0200 Subject: [PATCH 23/24] fix(iac): return checkov stderr as error log --- prowler/providers/iac/iac_provider.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/prowler/providers/iac/iac_provider.py b/prowler/providers/iac/iac_provider.py index c22a4daa5c..540bb827d0 100644 --- a/prowler/providers/iac/iac_provider.py +++ b/prowler/providers/iac/iac_provider.py @@ -159,11 +159,9 @@ class IacProvider(Provider): capture_output=True, text=True, ) - # Log Checkov's error output if any if process.stderr: - logger.critical(process.stderr) - sys.exit(1) + logger.error(process.stderr) try: output = json.loads(process.stdout) From 330903dc2771004306a16b84c3fc58d5b2121144 Mon Sep 17 00:00:00 2001 From: "Andoni A." <14891798+andoniaf@users.noreply.github.com> Date: Thu, 29 May 2025 19:02:51 +0200 Subject: [PATCH 24/24] fix(iac): fix tests --- tests/lib/outputs/finding_test.py | 2 +- tests/lib/outputs/ocsf/ocsf_test.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/lib/outputs/finding_test.py b/tests/lib/outputs/finding_test.py index 4d3fe43156..e4a290d426 100644 --- a/tests/lib/outputs/finding_test.py +++ b/tests/lib/outputs/finding_test.py @@ -3,7 +3,7 @@ from types import SimpleNamespace from unittest.mock import MagicMock, patch import pytest -from pydantic import ValidationError +from pydantic.v1 import ValidationError from prowler.lib.check.models import ( CheckMetadata, diff --git a/tests/lib/outputs/ocsf/ocsf_test.py b/tests/lib/outputs/ocsf/ocsf_test.py index 9f8d493069..bf48938471 100644 --- a/tests/lib/outputs/ocsf/ocsf_test.py +++ b/tests/lib/outputs/ocsf/ocsf_test.py @@ -6,9 +6,9 @@ import requests from freezegun import freeze_time from mock import patch from py_ocsf_models.events.base_event import SeverityID, StatusID -from py_ocsf_models.events.findings.detection_finding import DetectionFinding from py_ocsf_models.events.findings.detection_finding import ( - TypeID as DetectionFindingTypeID, + DetectionFinding, + DetectionFindingTypeID, ) from py_ocsf_models.events.findings.finding import ActivityID, FindingInformation from py_ocsf_models.objects.account import Account, TypeID @@ -174,7 +174,7 @@ class TestOCSF: "vendor_name": "Prowler", "version": prowler_version, }, - "version": "1.4.0", + "version": "1.5.0", "profiles": ["cloud", "datetime"], "tenant_uid": "test-organization-id", },