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/*" diff --git a/.github/workflows/sdk-pull-request.yml b/.github/workflows/sdk-pull-request.yml index 61768af5ef..646b72966e 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' 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/poetry.lock b/poetry.lock index d9c68ab5ea..9cc81262b6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "about-time" @@ -12,6 +12,21 @@ files = [ {file = "about_time-4.2.1-py3-none-any.whl", hash = "sha256:8bbf4c75fe13cbd3d72f49a03b02c5c7dca32169b6d49117c257e7eb3eaee341"}, ] +[[package]] +name = "aiodns" +version = "3.5.0" +description = "Simple DNS resolver for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiodns-3.5.0-py3-none-any.whl", hash = "sha256:6d0404f7d5215849233f6ee44854f2bb2481adf71b336b2279016ea5990ca5c5"}, + {file = "aiodns-3.5.0.tar.gz", hash = "sha256:11264edbab51896ecf546c18eb0dd56dff0428c6aa6d2cd87e643e07300eb310"}, +] + +[package.dependencies] +pycares = ">=4.9.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.44" +description = "Tool for detecting secrets in the codebase" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "bc_detect_secrets-1.5.44-py3-none-any.whl", hash = "sha256:0ab63d6c4f6680ec2dbe42cc3c63480568c55dbb6254afcc5bb6d4375a4e1d27"}, + {file = "bc_detect_secrets-1.5.44.tar.gz", hash = "sha256:bebd82c56055c600335f85db95f7ca3b434087f16292a0396a60705de1b94183"}, +] + +[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" @@ -1099,6 +1299,67 @@ files = [ {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, ] +[[package]] +name = "checkov" +version = "3.2.442" +description = "Infrastructure as code static analysis" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "checkov-3.2.442-py3-none-any.whl", hash = "sha256:e94a3283bff9b4a81e54e57b4a00b02259dec0d85b17bf17e00652d137bd1a6d"}, + {file = "checkov-3.2.442.tar.gz", hash = "sha256:e5206872de63d389cfb1b7c1212ce4b5a147986152e890461d87251726b4b0e7"}, +] + +[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.44" +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" +urllib3 = "1.26.20" +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" @@ -1114,6 +1375,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" @@ -1132,6 +1414,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" @@ -1144,6 +1450,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" @@ -1277,6 +1611,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" @@ -1365,6 +1722,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" @@ -1458,7 +1839,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"}, @@ -1475,6 +1856,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" @@ -1497,6 +1890,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" @@ -1767,7 +2172,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"}, @@ -1782,7 +2187,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"}, @@ -2088,28 +2493,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" @@ -2309,6 +2710,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" @@ -2337,6 +2753,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" @@ -2361,13 +2795,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"}, @@ -3138,44 +3591,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 = "nodeenv" @@ -3345,16 +3776,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]] @@ -3557,12 +4088,32 @@ 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 = "pre-commit" version = "4.2.0" @@ -3582,6 +4133,24 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[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" @@ -3774,20 +4343,20 @@ iamdata = ">=0.1.202504091" [[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" @@ -3804,6 +4373,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" @@ -3831,6 +4415,115 @@ files = [ [package.dependencies] pyasn1 = ">=0.6.1,<0.7.0" +[[package]] +name = "pycares" +version = "4.9.0" +description = "Python interface for c-ares" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pycares-4.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b8bd9a3ee6e9bc990e1933dc7e7e2f44d4184f49a90fa444297ac12ab6c0c84"}, + {file = "pycares-4.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:417a5c20861f35977240ad4961479a6778125bcac21eb2ad1c3aad47e2ff7fab"}, + {file = "pycares-4.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab290faa4ea53ce53e3ceea1b3a42822daffce2d260005533293a52525076750"}, + {file = "pycares-4.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1df81193084c9717734e4615e8c5074b9852478c9007d1a8bb242f7f580e67"}, + {file = "pycares-4.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20c7a6af0c2ccd17cc5a70d76e299a90e7ebd6c4d8a3d7fff5ae533339f61431"}, + {file = "pycares-4.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:370f41442a5b034aebdb2719b04ee04d3e805454a20d3f64f688c1c49f9137c3"}, + {file = "pycares-4.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:340e4a3bbfd14d73c01ec0793a321b8a4a93f64c508225883291078b7ee17ac8"}, + {file = "pycares-4.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f0ec94785856ea4f5556aa18f4c027361ba4b26cb36c4ad97d2105ef4eec68ba"}, + {file = "pycares-4.9.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6b7e23a4a9e2039b5d67dfa0499d2d5f114667dc13fb5d7d03eed230c7ac4f"}, + {file = "pycares-4.9.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:490c978b0be9d35a253a5e31dd598f6d66b453625f0eb7dc2d81b22b8c3bb3f4"}, + {file = "pycares-4.9.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e433faaf07f44e44f1a1b839fee847480fe3db9431509dafc9f16d618d491d0f"}, + {file = "pycares-4.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf6d8851a06b79d10089962c9dadcb34dad00bf027af000f7102297a54aaff2e"}, + {file = "pycares-4.9.0-cp310-cp310-win32.whl", hash = "sha256:4f803e7d66ac7d8342998b8b07393788991353a46b05bbaad0b253d6f3484ea8"}, + {file = "pycares-4.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e17bd32267e3870855de3baed7d0efa6337344d68f44853fd9195c919f39400"}, + {file = "pycares-4.9.0-cp310-cp310-win_arm64.whl", hash = "sha256:6b74f75d8e430f9bb11a1cc99b2e328eed74b17d8d4b476de09126f38d419eb9"}, + {file = "pycares-4.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:16a97ee83ec60d35c7f716f117719932c27d428b1bb56b242ba1c4aa55521747"}, + {file = "pycares-4.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:78748521423a211ce699a50c27cc5c19e98b7db610ccea98daad652ace373990"}, + {file = "pycares-4.9.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8818b2c7a57d9d6d41e8b64d9ff87992b8ea2522fc0799686725228bc3cff6c5"}, + {file = "pycares-4.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96df8990f16013ca5194d6ece19dddb4ef9cd7c3efaab9f196ec3ccd44b40f8d"}, + {file = "pycares-4.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61af86fd58b8326e723b0d20fb96b56acaec2261c3a7c9a1c29d0a79659d613a"}, + {file = "pycares-4.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec72edb276bda559813cc807bc47b423d409ffab2402417a5381077e9c2c6be1"}, + {file = "pycares-4.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832fb122c7376c76cab62f8862fa5e398b9575fb7c9ff6bc9811086441ee64ca"}, + {file = "pycares-4.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cdcfaef24f771a471671470ccfd676c0366ab6b0616fd8217b8f356c40a02b83"}, + {file = "pycares-4.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:52cb056d06ff55d78a8665b97ae948abaaba2ca200ca59b10346d4526bce1e7d"}, + {file = "pycares-4.9.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:54985ed3f2e8a87315269f24cb73441622857a7830adfc3a27c675a94c3261c1"}, + {file = "pycares-4.9.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:08048e223615d4aef3dac81fe0ea18fb18d6fc97881f1eb5be95bb1379969b8d"}, + {file = "pycares-4.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cc60037421ce05a409484287b2cd428e1363cca73c999b5f119936bb8f255208"}, + {file = "pycares-4.9.0-cp311-cp311-win32.whl", hash = "sha256:62b86895b60cfb91befb3086caa0792b53f949231c6c0c3053c7dfee3f1386ab"}, + {file = "pycares-4.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7046b3c80954beaabf2db52b09c3d6fe85f6c4646af973e61be79d1c51589932"}, + {file = "pycares-4.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:fcbda3fdf44e94d3962ca74e6ba3dc18c0d7029106f030d61c04c0876f319403"}, + {file = "pycares-4.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d68ca2da1001aeccdc81c4a2fb1f1f6cfdafd3d00e44e7c1ed93e3e05437f666"}, + {file = "pycares-4.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4f0c8fa5a384d79551a27eafa39eed29529e66ba8fa795ee432ab88d050432a3"}, + {file = "pycares-4.9.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb8c428cf3b9c6ff9c641ba50ab6357b4480cd737498733e6169b0ac8a1a89b"}, + {file = "pycares-4.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6845bd4a43abf6dab7fedbf024ef458ac3750a25b25076ea9913e5ac5fec4548"}, + {file = "pycares-4.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e28f4acc3b97e46610cf164665ebf914f709daea6ced0ca4358ce55bc1c3d6b"}, + {file = "pycares-4.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9464a39861840ce35a79352c34d653a9db44f9333af7c9feddb97998d3e00c07"}, + {file = "pycares-4.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0611c1bd46d1fc6bdd9305b8850eb84c77df485769f72c574ed7b8389dfbee2"}, + {file = "pycares-4.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4fb5a38a51d03b75ac4320357e632c2e72e03fdeb13263ee333a40621415fdc"}, + {file = "pycares-4.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:df5edae05fb3e1370ab7639e67e8891fdaa9026cb10f05dbd57893713f7a9cfe"}, + {file = "pycares-4.9.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:397123ea53d261007bb0aa7e767ef238778f45026db40bed8196436da2cc73de"}, + {file = "pycares-4.9.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bb0d874d0b131b29894fd8a0f842be91ac21d50f90ec04cff4bb3f598464b523"}, + {file = "pycares-4.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:497cc03a61ec1585eb17d2cb086a29a6a67d24babf1e9be519b47222916a3b06"}, + {file = "pycares-4.9.0-cp312-cp312-win32.whl", hash = "sha256:b46e46313fdb5e82da15478652aac0fd15e1c9f33e08153bad845aa4007d6f84"}, + {file = "pycares-4.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:12547a06445777091605a7581da15a0da158058beb8a05a3ebbf7301fd1f58d4"}, + {file = "pycares-4.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:f1e10bf1e8eb80b08e5c828627dba1ebc4acd54803bd0a27d92b9063b6aa99d8"}, + {file = "pycares-4.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:574d815112a95ab09d75d0a9dc7dea737c06985e3125cf31c32ba6a3ed6ca006"}, + {file = "pycares-4.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50e5ab06361d59625a27a7ad93d27e067dc7c9f6aa529a07d691eb17f3b43605"}, + {file = "pycares-4.9.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:785f5fd11ff40237d9bc8afa441551bb449e2812c74334d1d10859569e07515c"}, + {file = "pycares-4.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e194a500e403eba89b91fb863c917495c5b3dfcd1ce0ee8dc3a6f99a1360e2fc"}, + {file = "pycares-4.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:112dd49cdec4e6150a8d95b197e8b6b7b4468a3170b30738ed9b248cb2240c04"}, + {file = "pycares-4.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94aa3c2f3eb0aa69160137134775501f06c901188e722aac63d2a210d4084f99"}, + {file = "pycares-4.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b510d71255cf5a92ccc2643a553548fcb0623d6ed11c8c633b421d99d7fa4167"}, + {file = "pycares-4.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5c6aa30b1492b8130f7832bf95178642c710ce6b7ba610c2b17377f77177e3cd"}, + {file = "pycares-4.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e5767988e044faffe2aff6a76aa08df99a8b6ef2641be8b00ea16334ce5dea93"}, + {file = "pycares-4.9.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b9928a942820a82daa3207509eaba9e0fa9660756ac56667ec2e062815331fcb"}, + {file = "pycares-4.9.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:556c854174da76d544714cdfab10745ed5d4b99eec5899f7b13988cd26ff4763"}, + {file = "pycares-4.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d42e2202ca9aa9a0a9a6e43a4a4408bbe0311aaa44800fa27b8fd7f82b20152a"}, + {file = "pycares-4.9.0-cp313-cp313-win32.whl", hash = "sha256:cce8ef72c9ed4982c84114e6148a4e42e989d745de7862a0ad8b3f1cdc05def2"}, + {file = "pycares-4.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:318cdf24f826f1d2f0c5a988730bd597e1683296628c8f1be1a5b96643c284fe"}, + {file = "pycares-4.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:faa9de8e647ed06757a2c117b70a7645a755561def814da6aca0d766cf71a402"}, + {file = "pycares-4.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8310d27d68fa25be9781ce04d330f4860634a2ac34dd9265774b5f404679b41f"}, + {file = "pycares-4.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:99cf98452d3285307eec123049f2c9c50b109e06751b0727c6acefb6da30c6a0"}, + {file = "pycares-4.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ffd6e8c8250655504602b076f106653e085e6b1e15318013442558101aa4777"}, + {file = "pycares-4.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4065858d8c812159c9a55601fda73760d9e5e3300f7868d9e546eab1084f36c"}, + {file = "pycares-4.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91ee6818113faf9013945c2b54bcd6b123d0ac192ae3099cf4288cedaf2dbb25"}, + {file = "pycares-4.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21f0602059ec11857ab7ad608c7ec8bc6f7a302c04559ec06d33e82f040585f8"}, + {file = "pycares-4.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e22e5b46ed9b12183091da56e4a5a20813b5436c4f13135d7a1c20a84027ca8a"}, + {file = "pycares-4.9.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9eded8649867bfd7aea7589c5755eae4d37686272f6ed7a995da40890d02de71"}, + {file = "pycares-4.9.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f71d31cbbe066657a2536c98aad850724a9ab7b1cd2624f491832ae9667ea8e7"}, + {file = "pycares-4.9.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2b30945982ab4741f097efc5b0853051afc3c11df26996ed53a700c7575175af"}, + {file = "pycares-4.9.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54a8f1f067d64810426491d33033f5353b54f35e5339126440ad4e6afbf3f149"}, + {file = "pycares-4.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:41556a269a192349e92eee953f62eddd867e9eddb27f444b261e2c1c4a4a9eff"}, + {file = "pycares-4.9.0-cp39-cp39-win32.whl", hash = "sha256:524d6c14eaa167ed098a4fe54856d1248fa20c296cdd6976f9c1b838ba32d014"}, + {file = "pycares-4.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:15f930c733d36aa487b4ad60413013bd811281b5ea4ca620070fa38505d84df4"}, + {file = "pycares-4.9.0-cp39-cp39-win_arm64.whl", hash = "sha256:79b7addb2a41267d46650ac0d9c4f3b3233b036f186b85606f7586881dfb4b69"}, + {file = "pycares-4.9.0.tar.gz", hash = "sha256:8ee484ddb23dbec4d88d14ed5b6d592c1960d2e93c385d5e52b6fad564d82395"}, +] + +[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" @@ -3858,70 +4551,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.7" +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.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"}, + {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"}, ] [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" @@ -4083,6 +4843,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" @@ -4214,7 +5029,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"}, @@ -4313,6 +5128,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" @@ -4336,7 +5174,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"}, @@ -4775,6 +5613,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" @@ -4850,16 +5717,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" @@ -4945,7 +5831,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"}, @@ -4963,6 +5849,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" @@ -5023,6 +5962,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" @@ -5100,6 +6054,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" @@ -5130,6 +6106,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" @@ -5160,6 +6151,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" @@ -5172,6 +6175,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" @@ -5179,7 +6194,6 @@ description = "HTTP library with thread-safe connection pooling, file post, and optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" groups = ["main", "dev", "docs"] -markers = "python_version < \"3.10\"" files = [ {file = "urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e"}, {file = "urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32"}, @@ -5190,25 +6204,6 @@ brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and p secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -[[package]] -name = "urllib3" -version = "2.4.0" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.9" -groups = ["main", "dev", "docs"] -markers = "python_version >= \"3.10\"" -files = [ - {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, - {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - [[package]] name = "virtualenv" version = "20.31.2" @@ -5288,6 +6283,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" @@ -5430,7 +6437,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"}, @@ -5579,4 +6586,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">3.9.1,<3.13" -content-hash = "8c4c55b3206cdda399a5e0b0734fed5f89ba47b5b6efc899295ce7ccdf47583e" +content-hash = "e88f0c925f435849bf13d3a38978f71213033c2407708dcf3007fa0f79bc29a0" diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index f716ff44ad..49a812924b 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -27,8 +27,9 @@ All notable changes to the **Prowler SDK** are documented in this file. - `storage_ensure_file_shares_soft_delete_is_enabled` check for Azure provider [(#7966)](https://github.com/prowler-cloud/prowler/pull/7966) - Make `validate_mutelist` method static inside `Mutelist` class [(#7811)](https://github.com/prowler-cloud/prowler/pull/7811) - Avoid bypassing IAM check using wildcards [(#7708)](https://github.com/prowler-cloud/prowler/pull/7708) -- Add new method to authenticate in AppInsights in check `app_function_application_insights_enabled` [(#7763)](https://github.com/prowler-cloud/prowler/pull/7763) -- Add ISO 27001 2022 for M365 provider. [(#7985)](https://github.com/prowler-cloud/prowler/pull/7985) +- New method to authenticate in AppInsights in check `app_function_application_insights_enabled` [(#7763)](https://github.com/prowler-cloud/prowler/pull/7763) +- ISO 27001 2022 for M365 provider. [(#7985)](https://github.com/prowler-cloud/prowler/pull/7985) +- IaC provider [(#7852)](https://github.com/prowler-cloud/prowler/pull/7852) --- ## [v5.7.5] (Prowler UNRELEASED) diff --git a/prowler/__main__.py b/prowler/__main__.py index 0da3b95d68..8dec8188de 100644 --- a/prowler/__main__.py +++ b/prowler/__main__.py @@ -99,6 +99,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 @@ -176,11 +177,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 @@ -232,39 +235,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": @@ -295,6 +304,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: @@ -304,7 +315,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/compliance/kubernetes/cis_1.10_kubernetes.json b/prowler/compliance/kubernetes/cis_1.10_kubernetes.json index 379504a127..449de51208 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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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 d091e42bae..832533624e 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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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**.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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 88fcfd59b2..280375fc31 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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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.", @@ -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/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..0d0200ba63 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 @@ -56,22 +56,26 @@ 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" + 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): +class CIS_Requirement_Attribute_AssessmentStatus(str, Enum): """CIS Requirement Attribute Assessment Status""" Manual = "Manual" @@ -83,7 +87,7 @@ class CIS_Requirement_Attribute(BaseModel): """CIS Requirement Attribute""" Section: str - SubSection: Optional[str] + SubSection: Optional[str] = None Profile: CIS_Requirement_Attribute_Profile AssessmentStatus: CIS_Requirement_Attribute_AssessmentStatus Description: str @@ -92,7 +96,7 @@ class CIS_Requirement_Attribute(BaseModel): RemediationProcedure: str AuditProcedure: str AdditionalInformation: str - DefaultValue: Optional[str] + DefaultValue: Optional[str] = None References: str @@ -104,7 +108,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 @@ -177,10 +181,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 @@ -203,7 +207,7 @@ class Compliance_Requirement(BaseModel): Id: str Description: str - Name: Optional[str] + Name: Optional[str] = None Attributes: list[ Union[ CIS_Requirement_Attribute, @@ -224,7 +228,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 da670a9dda..59c0d31b4d 100644 --- a/prowler/lib/check/models.py +++ b/prowler/lib/check/models.py @@ -5,9 +5,9 @@ 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 +from pydantic.v1 import BaseModel, ValidationError, validator from prowler.config.config import Provider from prowler.lib.check.compliance_models import Compliance @@ -119,7 +119,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): @@ -614,6 +614,29 @@ class CheckReportM365(Check_Report): self.location = resource_location +@dataclass +class CheckReportIAC(Check_Report): + """Contains the IAC Check's finding information using Checkov.""" + + resource_name: str + resource_path: str + resource_line_range: 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.resource_name = getattr(finding, "resource", "") + self.resource_path = getattr(finding, "file_path", "") + self.resource_line_range = getattr(finding, "file_line_range", "") + + @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 746ec4e92a..02039c3c71 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,github,nhn,dashboard} ...", + usage="prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,github,nhn,dashboard,iac} ...", epilog=""" Available Cloud Providers: - {aws,azure,gcp,kubernetes,m365,nhn} + {aws,azure,gcp,kubernetes,m365,github,iac,nhn} aws AWS Provider azure Azure Provider gcp GCP Provider kubernetes Kubernetes Provider m365 Microsoft 365 Provider github GitHub Provider + iac IaC Provider (Preview) nhn NHN Provider (Unofficial) Available components: diff --git a/prowler/lib/outputs/asff/asff.py b/prowler/lib/outputs/asff/asff.py index ceb81e9ca2..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 @@ -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..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): @@ -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 1a4764c294..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): @@ -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 @@ -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 @@ -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/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 8cd38ec49a..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): @@ -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/iso27001/models.py b/prowler/lib/outputs/compliance/iso27001/models.py index 07b5cd7a53..d3b1429103 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 98b1f00a78..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): @@ -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/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 c8ac0dd783..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): @@ -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/lib/outputs/finding.py b/prowler/lib/outputs/finding.py index ffa0a2f44e..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 ( @@ -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) @@ -282,6 +282,18 @@ 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"] = "local" # Until we support remote repos + output_data["account_uid"] = "iac" + output_data["account_name"] = "iac" + 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 # TODO: in Azure, GCP and K8s there are findings without resource_name 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 Credentials +
+ +
+
""" + 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/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/aws/lib/arn/models.py b/prowler/providers/aws/lib/arn/models.py index ba0be84452..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 @@ -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/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 786d827e1a..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 @@ -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..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 @@ -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..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 @@ -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/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 32150b7313..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 @@ -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..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 @@ -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..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 @@ -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/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 f6d8f63365..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 @@ -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/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 ac05ebae10..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 @@ -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..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 @@ -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..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 @@ -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/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 7fc821a329..27af7b8019 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 cf0cd4be9b..752d1372c6 100644 --- a/prowler/providers/azure/models.py +++ b/prowler/providers/azure/models.py @@ -1,4 +1,6 @@ -from pydantic import BaseModel +from typing import Optional + +from pydantic.v1 import BaseModel from prowler.config.config import output_file_timestamp from prowler.providers.common.models import ProviderOutputOptions @@ -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/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 45f0c0128c..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 @@ -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/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/common/provider.py b/prowler/providers/common/provider.py index 9c97d63fbc..e7f4a74c2f 100644 --- a/prowler/providers/common/provider.py +++ b/prowler/providers/common/provider.py @@ -243,6 +243,12 @@ class Provider(ABC): mutelist_path=arguments.mutelist_file, config_path=arguments.config_file, ) + elif "iac" in provider_class_name.lower(): + provider_class( + scan_path=arguments.scan_path, + config_path=arguments.config_file, + fixer_config=fixer_config, + ) except TypeError as error: logger.critical( diff --git a/prowler/providers/gcp/models.py b/prowler/providers/gcp/models.py index 3505293fd8..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 @@ -14,14 +14,14 @@ 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): - number: str + number: int id: str name: str - organization: Optional[GCPOrganization] + organization: Optional[GCPOrganization] = None labels: dict lifecycle_state: str 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 0e2e74d27c..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 @@ -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/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 3ac498d2ba..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 @@ -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/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/iac/__init__.py b/prowler/providers/iac/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/iac/iac_provider.py b/prowler/providers/iac/iac_provider.py new file mode 100644 index 0000000000..540bb827d0 --- /dev/null +++ b/prowler/providers/iac/iac_provider.py @@ -0,0 +1,218 @@ +import json +import subprocess +import sys +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 +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 = {}, + ): + logger.info("Instantiating IAC Provider...") + + self.scan_path = scan_path + self.region = "global" + self.audited_account = "local-iac" + self._session = None + self._identity = "prowler" + + # 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( + 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 _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) + + 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, + ) + # Log Checkov's error output if any + if process.stderr: + logger.error(process.stderr) + + try: + output = json.loads(process.stdout) + if not output: + logger.warning("No findings returned from Checkov scan") + return [] + except Exception as error: + logger.critical( + f"{error.__class__.__name__}:{error.__traceback__.tb_lineno} -- {error}" + ) + sys.exit(1) + + reports = [] + + # 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: + results = finding.get("results", {}) + + # Process failed checks + failed_checks = results.get("failed_checks", []) + for failed_check in failed_checks: + report = self._process_check(finding, failed_check, "FAIL") + reports.append(report) + + # 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) + + # 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 + + 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..87401aecf1 --- /dev/null +++ b/prowler/providers/iac/lib/arguments/arguments.py @@ -0,0 +1,15 @@ +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" + ) + + # Scan Path + iac_scan_subparser = iac_parser.add_argument_group("Scan Path") + iac_scan_subparser.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/prowler/providers/kubernetes/services/core/core_service.py b/prowler/providers/kubernetes/services/core/core_service.py index ec6fb48ca8..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 @@ -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] diff --git a/prowler/providers/kubernetes/services/rbac/rbac_service.py b/prowler/providers/kubernetes/services/rbac/rbac_service.py index db8550c318..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 @@ -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/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/prowler/providers/m365/models.py b/prowler/providers/m365/models.py index f77bc8ee4c..6f9dc0e8c8 100644 --- a/prowler/providers/m365/models.py +++ b/prowler/providers/m365/models.py @@ -1,4 +1,6 @@ -from pydantic import BaseModel +from typing import Optional + +from pydantic.v1 import BaseModel from prowler.config.config import output_file_timestamp from prowler.providers.common.models import ProviderOutputOptions @@ -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 = [] diff --git a/prowler/providers/m365/services/admincenter/admincenter_service.py b/prowler/providers/m365/services/admincenter/admincenter_service.py index 32b2b12805..7c28ff15a2 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 05e152bf2d..e7159e1787 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/pyproject.toml b/pyproject.toml index 0838845c98..f78af76f10 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", + "checkov (>=3.2.434,<4.0.0)", "py-iam-expand==0.1.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." 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 diff --git a/tests/lib/cli/parser_test.py b/tests/lib/cli/parser_test.py index 2707d180af..75ff688718 100644 --- a/tests/lib/cli/parser_test.py +++ b/tests/lib/cli/parser_test.py @@ -17,11 +17,11 @@ 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,github,nhn,dashboard} ..." +prowler_default_usage_error = "usage: prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,github,nhn,dashboard,iac} ..." def mock_get_available_providers(): - return ["aws", "azure", "gcp", "kubernetes", "m365", "github", "nhn"] + return ["aws", "azure", "gcp", "kubernetes", "m365", "github", "iac", "nhn"] @pytest.mark.arg_parser 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 c656165195..3dcf26f2c4 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 ca3eef5ee0..cdd0be701a 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, @@ -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 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", }, diff --git a/tests/providers/azure/lib/mutelist/azure_mutelist_test.py b/tests/providers/azure/lib/mutelist/azure_mutelist_test.py index 7852aa54d2..83a981cbd2 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, ) diff --git a/tests/providers/gcp/lib/mutelist/gcp_mutelist_test.py b/tests/providers/gcp/lib/mutelist/gcp_mutelist_test.py index 01d3d1abfa..0285dd44f7 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/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..e2f3469569 --- /dev/null +++ b/tests/providers/iac/iac_provider_test.py @@ -0,0 +1,132 @@ +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 ( + 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="" + ) + + with pytest.raises(SystemExit) as excinfo: + provider.run_scan("/test/directory") + + assert excinfo.value.code == 1 + + @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 diff --git a/tests/providers/kubernetes/lib/mutelist/kubernetes_mutelist_test.py b/tests/providers/kubernetes/lib/mutelist/kubernetes_mutelist_test.py index 847c82aae8..366eb9ee3b 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, ) diff --git a/tests/providers/nhn/lib/mutelist/nhn_mutelist_test.py b/tests/providers/nhn/lib/mutelist/nhn_mutelist_test.py index 48444c622b..de0181c292 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, )