From 9954763356e5507be5cb5d0c9e2ec1b44d124fd1 Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Thu, 17 Nov 2022 22:59:28 +0100 Subject: [PATCH] feat(Lambda): Service and checks (#1491) --- Pipfile.lock | 2446 ++++++++--------- providers/aws/config.yaml | 19 +- providers/aws/services/awslambda/__init__.py | 0 .../services/awslambda/awslambda_client.py | 4 + .../__init__.py | 0 ...s_cloudtrail_logging_enabled.metadata.json | 35 + ...i_operations_cloudtrail_logging_enabled.py | 38 + ...rations_cloudtrail_logging_enabled_test.py | 211 ++ .../__init__.py | 0 ..._function_no_secrets_in_code.metadata.json | 35 + .../awslambda_function_no_secrets_in_code.py | 40 + ...lambda_function_no_secrets_in_code_test.py | 124 + .../__init__.py | 0 ...tion_no_secrets_in_variables.metadata.json | 35 + ...lambda_function_no_secrets_in_variables.py | 44 + ...a_function_no_secrets_in_variables_test.py | 146 + .../__init__.py | 0 ...tion_not_publicly_accessible.metadata.json | 35 + ...lambda_function_not_publicly_accessible.py | 42 + ...a_function_not_publicly_accessible_test.py | 189 ++ .../__init__.py | 0 ...bda_function_url_cors_policy.metadata.json | 35 + .../awslambda_function_url_cors_policy.py | 23 + ...awslambda_function_url_cors_policy_test.py | 163 ++ .../awslambda_function_url_public/__init__.py | 0 ...wslambda_function_url_public.metadata.json | 35 + .../awslambda_function_url_public.py | 25 + .../awslambda_function_url_public_test.py | 118 + .../__init__.py | 0 ...ion_using_supported_runtimes.metadata.json | 35 + ...ambda_function_using_supported_runtimes.py | 24 + ..._function_using_supported_runtimes_test.py | 126 + .../services/awslambda/awslambda_service.py | 158 ++ .../awslambda/awslambda_service_test.py | 226 ++ providers/aws/services/lambda/check_extra7145 | 53 - providers/aws/services/lambda/check_extra7179 | 68 - providers/aws/services/lambda/check_extra7180 | 81 - providers/aws/services/lambda/check_extra720 | 72 - providers/aws/services/lambda/check_extra759 | 69 - providers/aws/services/lambda/check_extra760 | 97 - providers/aws/services/lambda/check_extra762 | 52 - providers/aws/services/lambda/check_extra798 | 54 - 42 files changed, 3187 insertions(+), 1770 deletions(-) create mode 100644 providers/aws/services/awslambda/__init__.py create mode 100644 providers/aws/services/awslambda/awslambda_client.py create mode 100644 providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/__init__.py create mode 100644 providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.metadata.json create mode 100644 providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.py create mode 100644 providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled_test.py create mode 100644 providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/__init__.py create mode 100644 providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code.metadata.json create mode 100644 providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code.py create mode 100644 providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code_test.py create mode 100644 providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/__init__.py create mode 100644 providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables.metadata.json create mode 100644 providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables.py create mode 100644 providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables_test.py create mode 100644 providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/__init__.py create mode 100644 providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json create mode 100644 providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py create mode 100644 providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py create mode 100644 providers/aws/services/awslambda/awslambda_function_url_cors_policy/__init__.py create mode 100644 providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy.metadata.json create mode 100644 providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy.py create mode 100644 providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy_test.py create mode 100644 providers/aws/services/awslambda/awslambda_function_url_public/__init__.py create mode 100644 providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public.metadata.json create mode 100644 providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public.py create mode 100644 providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public_test.py create mode 100644 providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/__init__.py create mode 100644 providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes.metadata.json create mode 100644 providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes.py create mode 100644 providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes_test.py create mode 100644 providers/aws/services/awslambda/awslambda_service.py create mode 100644 providers/aws/services/awslambda/awslambda_service_test.py delete mode 100644 providers/aws/services/lambda/check_extra7145 delete mode 100644 providers/aws/services/lambda/check_extra7179 delete mode 100644 providers/aws/services/lambda/check_extra7180 delete mode 100644 providers/aws/services/lambda/check_extra720 delete mode 100644 providers/aws/services/lambda/check_extra759 delete mode 100644 providers/aws/services/lambda/check_extra760 delete mode 100644 providers/aws/services/lambda/check_extra762 delete mode 100644 providers/aws/services/lambda/check_extra798 diff --git a/Pipfile.lock b/Pipfile.lock index 74e5e14d..9da75af9 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,1228 +1,1228 @@ { - "_meta": { - "hash": { - "sha256": "881edb3306efd59b84c75bd2bff5acbc29397eb7f12321203c62a013f0491e2e" + "_meta": { + "hash": { + "sha256": "881edb3306efd59b84c75bd2bff5acbc29397eb7f12321203c62a013f0491e2e" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.9" + "default": { + "arnparse": { + "hashes": [ + "sha256:b0906734e4b8f19e39b1e32944c6cd6274b6da90c066a83882ac7a11d27553e0", + "sha256:cb87f17200d07121108a9085d4a09cc69a55582647776b9a917b0b1f279db8f8" + ], + "index": "pypi", + "version": "==0.0.2" + }, + "boto3": { + "hashes": [ + "sha256:3309d04e47490a1b46aa8ae7bcf4845a8dae2ed419a505c770895773ba7455dc", + "sha256:f8f7268afe62e3b34b7d0e1b4d8a804148e713f35b0e6dbc736346b28b3c6549" + ], + "index": "pypi", + "version": "==1.26.11" + }, + "botocore": { + "hashes": [ + "sha256:97532c8a571017891ad4afee7951bea95c9f0bfe96a6f38fdca9062cd6067ec1", + "sha256:a86d7e558074e849933983bc2c4cc6ae6bbed3aef3f5fb4d4689cd19331e4399" + ], + "index": "pypi", + "version": "==1.29.11" + }, + "certifi": { + "hashes": [ + "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14", + "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382" + ], + "markers": "python_version >= '3.6'", + "version": "==2022.9.24" + }, + "charset-normalizer": { + "hashes": [ + "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", + "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" + ], + "markers": "python_version >= '3.6'", + "version": "==2.1.1" + }, + "click": { + "hashes": [ + "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", + "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.3" + }, + "click-plugins": { + "hashes": [ + "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", + "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8" + ], + "version": "==1.1.1" + }, + "colorama": { + "hashes": [ + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" + ], + "index": "pypi", + "version": "==0.4.6" + }, + "detect-secrets": { + "hashes": [ + "sha256:d08ecabeee8b68c0acb0e8a354fb98d822a653f6ed05e520cead4c6fc1fc02cd", + "sha256:d56787e339758cef48c9ccd6692f7a094b9963c979c9813580b0169e41132833" + ], + "index": "pypi", + "version": "==1.4.0" + }, + "idna": { + "hashes": [ + "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + ], + "markers": "python_version >= '3.5'", + "version": "==3.4" + }, + "jmespath": { + "hashes": [ + "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", + "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" + ], + "markers": "python_version >= '3.7'", + "version": "==1.0.1" + }, + "pydantic": { + "hashes": [ + "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42", + "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624", + "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e", + "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559", + "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709", + "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9", + "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d", + "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52", + "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda", + "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912", + "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c", + "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525", + "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe", + "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41", + "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b", + "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283", + "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965", + "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c", + "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410", + "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5", + "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116", + "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98", + "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f", + "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644", + "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13", + "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd", + "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254", + "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6", + "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488", + "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5", + "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c", + "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1", + "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a", + "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2", + "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d", + "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236" + ], + "index": "pypi", + "version": "==1.10.2" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" + }, + "pyyaml": { + "hashes": [ + "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", + "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", + "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", + "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", + "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", + "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", + "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", + "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", + "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", + "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", + "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", + "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", + "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", + "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", + "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", + "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", + "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", + "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", + "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", + "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", + "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", + "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", + "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", + "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", + "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", + "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", + "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", + "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", + "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", + "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", + "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", + "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", + "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", + "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", + "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", + "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", + "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", + "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", + "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", + "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" + ], + "markers": "python_version >= '3.6'", + "version": "==6.0" + }, + "requests": { + "hashes": [ + "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", + "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" + ], + "markers": "python_version >= '3.7' and python_version < '4'", + "version": "==2.28.1" + }, + "s3transfer": { + "hashes": [ + "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd", + "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947" + ], + "markers": "python_version >= '3.7'", + "version": "==0.6.0" + }, + "shodan": { + "hashes": [ + "sha256:18bd2ae81114b70836e0e3315227325e14398275223998a8c235b099432f4b0b" + ], + "index": "pypi", + "version": "==1.28.0" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "typing-extensions": { + "hashes": [ + "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", + "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" + ], + "markers": "python_version >= '3.7'", + "version": "==4.4.0" + }, + "urllib3": { + "hashes": [ + "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", + "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", + "version": "==1.26.12" + }, + "xlsxwriter": { + "hashes": [ + "sha256:df0aefe5137478d206847eccf9f114715e42aaea077e6a48d0e8a2152e983010", + "sha256:e89f4a1d2fa2c9ea15cde77de95cd3fd8b0345d0efb3964623f395c8c4988b7f" + ], + "markers": "python_version >= '3.4'", + "version": "==3.0.3" + } }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "arnparse": { - "hashes": [ - "sha256:b0906734e4b8f19e39b1e32944c6cd6274b6da90c066a83882ac7a11d27553e0", - "sha256:cb87f17200d07121108a9085d4a09cc69a55582647776b9a917b0b1f279db8f8" - ], - "index": "pypi", - "version": "==0.0.2" - }, - "boto3": { - "hashes": [ - "sha256:15b059251990706c5d5a556c42a6e6e781a51edadf48a42afdd06bd313adfaf2", - "sha256:27efa5eb229364bc4643d9e182de0891f73c21b65ef80b5bd02a977caeaf595c" - ], - "index": "pypi", - "version": "==1.26.9" - }, - "botocore": { - "hashes": [ - "sha256:01f168e2418419a6d8b335ecc4330faa6a7332d5a097029fc9b4a3ae3c41cea2", - "sha256:c876a7c6a07d7f86f46a8cc765f0d38999b84ac1d7b0666d6ce08c0503e13145" - ], - "index": "pypi", - "version": "==1.29.9" - }, - "certifi": { - "hashes": [ - "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14", - "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==2022.9.24" - }, - "charset-normalizer": { - "hashes": [ - "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", - "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==2.1.1" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "click-plugins": { - "hashes": [ - "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b", - "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8" - ], - "version": "==1.1.1" - }, - "colorama": { - "hashes": [ - "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", - "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" - ], - "index": "pypi", - "version": "==0.4.6" - }, - "detect-secrets": { - "hashes": [ - "sha256:d08ecabeee8b68c0acb0e8a354fb98d822a653f6ed05e520cead4c6fc1fc02cd", - "sha256:d56787e339758cef48c9ccd6692f7a094b9963c979c9813580b0169e41132833" - ], - "index": "pypi", - "version": "==1.4.0" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "jmespath": { - "hashes": [ - "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", - "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" - ], - "markers": "python_version >= '3.7'", - "version": "==1.0.1" - }, - "pydantic": { - "hashes": [ - "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42", - "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624", - "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e", - "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559", - "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709", - "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9", - "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d", - "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52", - "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda", - "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912", - "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c", - "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525", - "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe", - "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41", - "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b", - "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283", - "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965", - "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c", - "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410", - "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5", - "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116", - "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98", - "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f", - "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644", - "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13", - "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd", - "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254", - "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6", - "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488", - "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5", - "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c", - "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1", - "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a", - "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2", - "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d", - "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236" - ], - "index": "pypi", - "version": "==1.10.2" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pyyaml": { - "hashes": [ - "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==6.0" - }, - "requests": { - "hashes": [ - "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", - "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" - ], - "markers": "python_version >= '3.7' and python_version < '4'", - "version": "==2.28.1" - }, - "s3transfer": { - "hashes": [ - "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd", - "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947" - ], - "markers": "python_version >= '3.7'", - "version": "==0.6.0" - }, - "shodan": { - "hashes": [ - "sha256:18bd2ae81114b70836e0e3315227325e14398275223998a8c235b099432f4b0b" - ], - "index": "pypi", - "version": "==1.28.0" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "typing-extensions": { - "hashes": [ - "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", - "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" - ], - "markers": "python_version >= '3.7'", - "version": "==4.4.0" - }, - "urllib3": { - "hashes": [ - "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", - "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", - "version": "==1.26.12" - }, - "xlsxwriter": { - "hashes": [ - "sha256:df0aefe5137478d206847eccf9f114715e42aaea077e6a48d0e8a2152e983010", - "sha256:e89f4a1d2fa2c9ea15cde77de95cd3fd8b0345d0efb3964623f395c8c4988b7f" - ], - "markers": "python_version >= '3.4'", - "version": "==3.0.3" + "develop": { + "astroid": { + "hashes": [ + "sha256:1c00a14f5a3ed0339d38d2e2e5b74ea2591df5861c0936bb292b84ccf3a78d83", + "sha256:72702205200b2a638358369d90c222d74ebc376787af8fb2f7f2a86f7b5cc85f" + ], + "markers": "python_full_version >= '3.7.2'", + "version": "==2.12.12" + }, + "attrs": { + "hashes": [ + "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6", + "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c" + ], + "markers": "python_version >= '3.5'", + "version": "==22.1.0" + }, + "bandit": { + "hashes": [ + "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2", + "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a" + ], + "index": "pypi", + "version": "==1.7.4" + }, + "black": { + "hashes": [ + "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7", + "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6", + "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650", + "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb", + "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d", + "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d", + "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de", + "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395", + "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae", + "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa", + "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef", + "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383", + "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66", + "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87", + "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d", + "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0", + "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b", + "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458", + "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4", + "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1", + "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff" + ], + "index": "pypi", + "version": "==22.10.0" + }, + "boto3": { + "hashes": [ + "sha256:3309d04e47490a1b46aa8ae7bcf4845a8dae2ed419a505c770895773ba7455dc", + "sha256:f8f7268afe62e3b34b7d0e1b4d8a804148e713f35b0e6dbc736346b28b3c6549" + ], + "index": "pypi", + "version": "==1.26.11" + }, + "botocore": { + "hashes": [ + "sha256:97532c8a571017891ad4afee7951bea95c9f0bfe96a6f38fdca9062cd6067ec1", + "sha256:a86d7e558074e849933983bc2c4cc6ae6bbed3aef3f5fb4d4689cd19331e4399" + ], + "index": "pypi", + "version": "==1.29.11" + }, + "certifi": { + "hashes": [ + "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14", + "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382" + ], + "markers": "python_version >= '3.6'", + "version": "==2022.9.24" + }, + "cffi": { + "hashes": [ + "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", + "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", + "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", + "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", + "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", + "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", + "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", + "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", + "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", + "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", + "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", + "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", + "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", + "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", + "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", + "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", + "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", + "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", + "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", + "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", + "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", + "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", + "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", + "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", + "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", + "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", + "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", + "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", + "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", + "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", + "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", + "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", + "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", + "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", + "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", + "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", + "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", + "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", + "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", + "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", + "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", + "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", + "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", + "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", + "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", + "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", + "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", + "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", + "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", + "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", + "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", + "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", + "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", + "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", + "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", + "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", + "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", + "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", + "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", + "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", + "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", + "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", + "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", + "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" + ], + "version": "==1.15.1" + }, + "charset-normalizer": { + "hashes": [ + "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", + "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" + ], + "markers": "python_version >= '3.6'", + "version": "==2.1.1" + }, + "click": { + "hashes": [ + "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", + "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.3" + }, + "coverage": { + "hashes": [ + "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79", + "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a", + "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f", + "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a", + "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa", + "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398", + "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba", + "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d", + "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf", + "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b", + "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518", + "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d", + "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795", + "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2", + "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e", + "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32", + "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745", + "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b", + "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e", + "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d", + "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f", + "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660", + "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62", + "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6", + "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04", + "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c", + "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5", + "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef", + "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc", + "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae", + "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578", + "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466", + "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4", + "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91", + "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0", + "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4", + "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b", + "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe", + "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b", + "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75", + "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b", + "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c", + "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72", + "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b", + "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f", + "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e", + "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53", + "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3", + "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84", + "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987" + ], + "index": "pypi", + "version": "==6.5.0" + }, + "cryptography": { + "hashes": [ + "sha256:068147f32fa662c81aebab95c74679b401b12b57494872886eb5c1139250ec5d", + "sha256:06fc3cc7b6f6cca87bd56ec80a580c88f1da5306f505876a71c8cfa7050257dd", + "sha256:25c1d1f19729fb09d42e06b4bf9895212292cb27bb50229f5aa64d039ab29146", + "sha256:402852a0aea73833d982cabb6d0c3bb582c15483d29fb7085ef2c42bfa7e38d7", + "sha256:4e269dcd9b102c5a3d72be3c45d8ce20377b8076a43cbed6f660a1afe365e436", + "sha256:5419a127426084933076132d317911e3c6eb77568a1ce23c3ac1e12d111e61e0", + "sha256:554bec92ee7d1e9d10ded2f7e92a5d70c1f74ba9524947c0ba0c850c7b011828", + "sha256:5e89468fbd2fcd733b5899333bc54d0d06c80e04cd23d8c6f3e0542358c6060b", + "sha256:65535bc550b70bd6271984d9863a37741352b4aad6fb1b3344a54e6950249b55", + "sha256:6ab9516b85bebe7aa83f309bacc5f44a61eeb90d0b4ec125d2d003ce41932d36", + "sha256:6addc3b6d593cd980989261dc1cce38263c76954d758c3c94de51f1e010c9a50", + "sha256:728f2694fa743a996d7784a6194da430f197d5c58e2f4e278612b359f455e4a2", + "sha256:785e4056b5a8b28f05a533fab69febf5004458e20dad7e2e13a3120d8ecec75a", + "sha256:78cf5eefac2b52c10398a42765bfa981ce2372cbc0457e6bf9658f41ec3c41d8", + "sha256:7f836217000342d448e1c9a342e9163149e45d5b5eca76a30e84503a5a96cab0", + "sha256:8d41a46251bf0634e21fac50ffd643216ccecfaf3701a063257fe0b2be1b6548", + "sha256:984fe150f350a3c91e84de405fe49e688aa6092b3525f407a18b9646f6612320", + "sha256:9b24bcff7853ed18a63cfb0c2b008936a9554af24af2fb146e16d8e1aed75748", + "sha256:b1b35d9d3a65542ed2e9d90115dfd16bbc027b3f07ee3304fc83580f26e43249", + "sha256:b1b52c9e5f8aa2b802d48bd693190341fae201ea51c7a167d69fc48b60e8a959", + "sha256:bbf203f1a814007ce24bd4d51362991d5cb90ba0c177a9c08825f2cc304d871f", + "sha256:be243c7e2bfcf6cc4cb350c0d5cdf15ca6383bbcb2a8ef51d3c9411a9d4386f0", + "sha256:bfbe6ee19615b07a98b1d2287d6a6073f734735b49ee45b11324d85efc4d5cbd", + "sha256:c46837ea467ed1efea562bbeb543994c2d1f6e800785bd5a2c98bc096f5cb220", + "sha256:dfb4f4dd568de1b6af9f4cda334adf7d72cf5bc052516e1b2608b683375dd95c", + "sha256:ed7b00096790213e09eb11c97cc6e2b757f15f3d2f85833cd2d3ec3fe37c1722" + ], + "markers": "python_version >= '3.6'", + "version": "==38.0.3" + }, + "dill": { + "hashes": [ + "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0", + "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==0.3.6" + }, + "docker": { + "hashes": [ + "sha256:896c4282e5c7af5c45e8b683b0b0c33932974fe6e50fc6906a0a83616ab3da97", + "sha256:dbcb3bd2fa80dca0788ed908218bf43972772009b881ed1e20dfc29a65e49782" + ], + "index": "pypi", + "version": "==6.0.1" + }, + "dparse": { + "hashes": [ + "sha256:8097076f1dd26c377f30d4745e6ec18fef42f3bf493933b842ac5bafad8c345f", + "sha256:d45255bda21f998bc7ddf2afd5e62505ba6134756ba2d42a84c56b0826614dfe" + ], + "markers": "python_version >= '3.5'", + "version": "==0.6.2" + }, + "exceptiongroup": { + "hashes": [ + "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828", + "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec" + ], + "markers": "python_version < '3.11'", + "version": "==1.0.4" + }, + "execnet": { + "hashes": [ + "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5", + "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==1.9.0" + }, + "flake8": { + "hashes": [ + "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db", + "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248" + ], + "index": "pypi", + "version": "==5.0.4" + }, + "freezegun": { + "hashes": [ + "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446", + "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f" + ], + "index": "pypi", + "version": "==1.2.2" + }, + "gitdb": { + "hashes": [ + "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd", + "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa" + ], + "markers": "python_version >= '3.6'", + "version": "==4.0.9" + }, + "gitpython": { + "hashes": [ + "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f", + "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.1.29" + }, + "idna": { + "hashes": [ + "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", + "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" + ], + "markers": "python_version >= '3.5'", + "version": "==3.4" + }, + "importlib-resources": { + "hashes": [ + "sha256:c01b1b94210d9849f286b86bb51bcea7cd56dde0600d8db721d7b81330711668", + "sha256:ee17ec648f85480d523596ce49eae8ead87d5631ae1551f913c0100b5edd3437" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==5.10.0" + }, + "iniconfig": { + "hashes": [ + "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", + "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" + ], + "version": "==1.1.1" + }, + "isort": { + "hashes": [ + "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7", + "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951" + ], + "markers": "python_full_version >= '3.6.1' and python_full_version < '4.0.0'", + "version": "==5.10.1" + }, + "jinja2": { + "hashes": [ + "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", + "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==3.1.2" + }, + "jmespath": { + "hashes": [ + "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", + "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" + ], + "markers": "python_version >= '3.7'", + "version": "==1.0.1" + }, + "jsonschema": { + "hashes": [ + "sha256:5bfcf2bca16a087ade17e02b282d34af7ccd749ef76241e7f9bd7c0cb8a9424d", + "sha256:f660066c3966db7d6daeaea8a75e0b68237a48e51cf49882087757bb59916248" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==4.17.0" + }, + "jsonschema-spec": { + "hashes": [ + "sha256:1e525177574c23ae0f55cd62382632a083a0339928f0ca846a975a4da9851cec", + "sha256:780a22d517cdc857d9714a80d8349c546945063f20853ea32ba7f85bc643ec7d" + ], + "markers": "python_full_version >= '3.7.0' and python_full_version < '4.0.0'", + "version": "==0.1.2" + }, + "lazy-object-proxy": { + "hashes": [ + "sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada", + "sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d", + "sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7", + "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe", + "sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd", + "sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c", + "sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858", + "sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288", + "sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec", + "sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f", + "sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891", + "sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c", + "sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25", + "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156", + "sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8", + "sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f", + "sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e", + "sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0", + "sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==1.8.0" + }, + "markupsafe": { + "hashes": [ + "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", + "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", + "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", + "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", + "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", + "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", + "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", + "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", + "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", + "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", + "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", + "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", + "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", + "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", + "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", + "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", + "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", + "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", + "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", + "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", + "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", + "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", + "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", + "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", + "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", + "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", + "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", + "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", + "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", + "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", + "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", + "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", + "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", + "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", + "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", + "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", + "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", + "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", + "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", + "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==2.1.1" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "mock": { + "hashes": [ + "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62", + "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc" + ], + "markers": "python_version >= '3.6'", + "version": "==4.0.3" + }, + "moto": { + "hashes": [ + "sha256:2fb909d2ea1b732f89604e4268e2c2207c253e590a635a410c3c2aaebb34e113", + "sha256:ba03b638cf3b1cec64cbe9ac0d184ca898b69020c8e3c5b9b4961c1670629010" + ], + "index": "pypi", + "version": "==4.0.9" + }, + "mypy-extensions": { + "hashes": [ + "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", + "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + ], + "version": "==0.4.3" + }, + "openapi-schema-validator": { + "hashes": [ + "sha256:34fbd14b7501abe25e64d7b4624a9db02cde1a578d285b3da6f34b290cdf0b3a", + "sha256:7cf27585dd7970b7257cefe48e1a3a10d4e34421831bdb472d96967433bc27bd" + ], + "markers": "python_full_version >= '3.7.0' and python_full_version < '4.0.0'", + "version": "==0.3.4" + }, + "openapi-spec-validator": { + "hashes": [ + "sha256:4a8aee1e45b1ac868e07ab25e18828fe9837baddd29a8e20fdb3d3c61c8eea3d", + "sha256:8248634bad1f23cac5d5a34e193ab36e23914057ca69e91a1ede5af75552c465" + ], + "index": "pypi", + "version": "==0.5.1" + }, + "packaging": { + "hashes": [ + "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", + "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + ], + "markers": "python_version >= '3.6'", + "version": "==21.3" + }, + "pathable": { + "hashes": [ + "sha256:5c869d315be50776cc8a993f3af43e0c60dc01506b399643f919034ebf4cdcab", + "sha256:cdd7b1f9d7d5c8b8d3315dbf5a86b2596053ae845f056f57d97c0eefff84da14" + ], + "markers": "python_full_version >= '3.7.0' and python_full_version < '4.0.0'", + "version": "==0.4.3" + }, + "pathspec": { + "hashes": [ + "sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5", + "sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==0.10.2" + }, + "pbr": { + "hashes": [ + "sha256:b97bc6695b2aff02144133c2e7399d5885223d42b7912ffaec2ca3898e673bfe", + "sha256:db2317ff07c84c4c63648c9064a79fe9d9f5c7ce85a9099d4b6258b3db83225a" + ], + "markers": "python_version >= '2.6'", + "version": "==5.11.0" + }, + "platformdirs": { + "hashes": [ + "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7", + "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==2.5.4" + }, + "pluggy": { + "hashes": [ + "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", + "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" + ], + "markers": "python_version >= '3.6'", + "version": "==1.0.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785", + "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b" + ], + "markers": "python_version >= '3.6'", + "version": "==2.9.1" + }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" + }, + "pyflakes": { + "hashes": [ + "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2", + "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3" + ], + "markers": "python_version >= '3.6'", + "version": "==2.5.0" + }, + "pylint": { + "hashes": [ + "sha256:3b120505e5af1d06a5ad76b55d8660d44bf0f2fc3c59c2bdd94e39188ee3a4df", + "sha256:c2108037eb074334d9e874dc3c783752cc03d0796c88c9a9af282d0f161a1004" + ], + "index": "pypi", + "version": "==2.15.5" + }, + "pyparsing": { + "hashes": [ + "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", + "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" + ], + "markers": "python_full_version >= '3.6.8'", + "version": "==3.0.9" + }, + "pyrsistent": { + "hashes": [ + "sha256:055ab45d5911d7cae397dc418808d8802fb95262751872c841c170b0dbf51eed", + "sha256:111156137b2e71f3a9936baf27cb322e8024dac3dc54ec7fb9f0bcf3249e68bb", + "sha256:187d5730b0507d9285a96fca9716310d572e5464cadd19f22b63a6976254d77a", + "sha256:21455e2b16000440e896ab99e8304617151981ed40c29e9507ef1c2e4314ee95", + "sha256:2aede922a488861de0ad00c7630a6e2d57e8023e4be72d9d7147a9fcd2d30712", + "sha256:3ba4134a3ff0fc7ad225b6b457d1309f4698108fb6b35532d015dca8f5abed73", + "sha256:456cb30ca8bff00596519f2c53e42c245c09e1a4543945703acd4312949bfd41", + "sha256:71d332b0320642b3261e9fee47ab9e65872c2bd90260e5d225dabeed93cbd42b", + "sha256:879b4c2f4d41585c42df4d7654ddffff1239dc4065bc88b745f0341828b83e78", + "sha256:9cd3e9978d12b5d99cbdc727a3022da0430ad007dacf33d0bf554b96427f33ab", + "sha256:a178209e2df710e3f142cbd05313ba0c5ebed0a55d78d9945ac7a4e09d923308", + "sha256:b39725209e06759217d1ac5fcdb510e98670af9e37223985f330b611f62e7425", + "sha256:bfa0351be89c9fcbcb8c9879b826f4353be10f58f8a677efab0c017bf7137ec2", + "sha256:bfd880614c6237243ff53a0539f1cb26987a6dc8ac6e66e0c5a40617296a045e", + "sha256:c43bec251bbd10e3cb58ced80609c5c1eb238da9ca78b964aea410fb820d00d6", + "sha256:d690b18ac4b3e3cab73b0b7aa7dbe65978a172ff94970ff98d82f2031f8971c2", + "sha256:d6982b5a0237e1b7d876b60265564648a69b14017f3b5f908c5be2de3f9abb7a", + "sha256:dec3eac7549869365fe263831f576c8457f6c833937c68542d08fde73457d291", + "sha256:e371b844cec09d8dc424d940e54bba8f67a03ebea20ff7b7b0d56f526c71d584", + "sha256:e5d8f84d81e3729c3b506657dddfe46e8ba9c330bf1858ee33108f8bb2adb38a", + "sha256:ea6b79a02a28550c98b6ca9c35b9f492beaa54d7c5c9e9949555893c8a9234d0", + "sha256:f1258f4e6c42ad0b20f9cfcc3ada5bd6b83374516cd01c0960e3cb75fdca6770" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==0.19.2" + }, + "pytest": { + "hashes": [ + "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71", + "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59" + ], + "index": "pypi", + "version": "==7.2.0" + }, + "pytest-xdist": { + "hashes": [ + "sha256:688da9b814370e891ba5de650c9327d1a9d861721a524eb917e620eec3e90291", + "sha256:9feb9a18e1790696ea23e1434fa73b325ed4998b0e9fcb221f16fd1945e6df1b" + ], + "index": "pypi", + "version": "==3.0.2" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.8.2" + }, + "pytz": { + "hashes": [ + "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427", + "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2" + ], + "version": "==2022.6" + }, + "pyyaml": { + "hashes": [ + "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", + "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", + "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", + "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", + "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", + "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", + "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", + "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", + "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", + "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", + "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", + "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", + "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", + "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", + "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", + "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", + "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", + "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", + "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", + "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", + "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", + "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", + "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", + "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", + "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", + "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", + "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", + "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", + "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", + "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", + "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", + "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", + "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", + "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", + "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", + "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", + "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", + "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", + "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", + "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" + ], + "markers": "python_version >= '3.6'", + "version": "==6.0" + }, + "requests": { + "hashes": [ + "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", + "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" + ], + "markers": "python_version >= '3.7' and python_version < '4'", + "version": "==2.28.1" + }, + "responses": { + "hashes": [ + "sha256:396acb2a13d25297789a5866b4881cf4e46ffd49cc26c43ab1117f40b973102e", + "sha256:dcf294d204d14c436fddcc74caefdbc5764795a40ff4e6a7740ed8ddbf3294be" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==0.22.0" + }, + "ruamel.yaml": { + "hashes": [ + "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7", + "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af" + ], + "markers": "python_version >= '3'", + "version": "==0.17.21" + }, + "ruamel.yaml.clib": { + "hashes": [ + "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e", + "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3", + "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5", + "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497", + "sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f", + "sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac", + "sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697", + "sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763", + "sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282", + "sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1", + "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072", + "sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9", + "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5", + "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231", + "sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93", + "sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b", + "sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb", + "sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f", + "sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307", + "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8", + "sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b", + "sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b", + "sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640", + "sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7", + "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a", + "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71", + "sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8", + "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7", + "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80", + "sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e", + "sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab", + "sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0", + "sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646" + ], + "markers": "python_version < '3.11' and platform_python_implementation == 'CPython'", + "version": "==0.2.7" + }, + "s3transfer": { + "hashes": [ + "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd", + "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947" + ], + "markers": "python_version >= '3.7'", + "version": "==0.6.0" + }, + "safety": { + "hashes": [ + "sha256:6e6fcb7d4e8321098cf289f59b65051cafd3467f089c6e57c9f894ae32c23b71", + "sha256:8f098d12b607db2756886280e85c28ece8db1bba4f45fc5f981f4663217bd619" + ], + "index": "pypi", + "version": "==2.3.1" + }, + "setuptools": { + "hashes": [ + "sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31", + "sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==65.5.1" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" + }, + "smmap": { + "hashes": [ + "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94", + "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936" + ], + "markers": "python_version >= '3.6'", + "version": "==5.0.0" + }, + "stevedore": { + "hashes": [ + "sha256:7f8aeb6e3f90f96832c301bff21a7eb5eefbe894c88c506483d355565d88cc1a", + "sha256:aa6436565c069b2946fe4ebff07f5041e0c8bf18c7376dd29edf80cf7d524e4e" + ], + "markers": "python_version >= '3.8'", + "version": "==4.1.1" + }, + "sure": { + "hashes": [ + "sha256:34ae88c846046742ef074036bf311dc90ab152b7bc09c342b281cebf676727a2" + ], + "index": "pypi", + "version": "==2.0.0" + }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==0.10.2" + }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_full_version < '3.11.0a7'", + "version": "==2.0.1" + }, + "tomlkit": { + "hashes": [ + "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b", + "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73" + ], + "markers": "python_version >= '3.6'", + "version": "==0.11.6" + }, + "types-toml": { + "hashes": [ + "sha256:171bdb3163d79a520560f24ba916a9fc9bff81659c5448a9fea89240923722be", + "sha256:b7b5c4977f96ab7b5ac06d8a6590d17c0bf252a96efc03b109c2711fb3e0eafd" + ], + "version": "==0.10.8.1" + }, + "typing-extensions": { + "hashes": [ + "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", + "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" + ], + "markers": "python_version >= '3.7'", + "version": "==4.4.0" + }, + "urllib3": { + "hashes": [ + "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", + "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", + "version": "==1.26.12" + }, + "vulture": { + "hashes": [ + "sha256:2515fa848181001dc8a73aba6a01a1a17406f5d372f24ec7f7191866f9f4997e", + "sha256:e792e903ccc063ec4873a8979dcf11b51ea3d65a2d3b31c113d47be48f0cdcae" + ], + "index": "pypi", + "version": "==2.6" + }, + "websocket-client": { + "hashes": [ + "sha256:d6b06432f184438d99ac1f456eaf22fe1ade524c3dd16e661142dc54e9cba574", + "sha256:d6e8f90ca8e2dd4e8027c4561adeb9456b54044312dba655e7cae652ceb9ae59" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==1.4.2" + }, + "werkzeug": { + "hashes": [ + "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", + "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5" + ], + "markers": "python_full_version >= '3.7.0'", + "version": "==2.2.2" + }, + "wrapt": { + "hashes": [ + "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3", + "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b", + "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4", + "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2", + "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656", + "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3", + "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff", + "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310", + "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a", + "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57", + "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069", + "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383", + "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe", + "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87", + "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d", + "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b", + "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907", + "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f", + "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0", + "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28", + "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1", + "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853", + "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc", + "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3", + "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3", + "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164", + "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1", + "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c", + "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1", + "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7", + "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1", + "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320", + "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed", + "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1", + "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248", + "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c", + "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456", + "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77", + "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef", + "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1", + "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7", + "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86", + "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4", + "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d", + "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d", + "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8", + "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5", + "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471", + "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00", + "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68", + "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3", + "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d", + "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735", + "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d", + "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569", + "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7", + "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59", + "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5", + "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb", + "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b", + "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f", + "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462", + "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015", + "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af" + ], + "markers": "python_version < '3.11'", + "version": "==1.14.1" + }, + "xmltodict": { + "hashes": [ + "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56", + "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852" + ], + "markers": "python_version >= '3.4'", + "version": "==0.13.0" + }, + "zipp": { + "hashes": [ + "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1", + "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8" + ], + "markers": "python_version < '3.10'", + "version": "==3.10.0" + } } - }, - "develop": { - "astroid": { - "hashes": [ - "sha256:1c00a14f5a3ed0339d38d2e2e5b74ea2591df5861c0936bb292b84ccf3a78d83", - "sha256:72702205200b2a638358369d90c222d74ebc376787af8fb2f7f2a86f7b5cc85f" - ], - "markers": "python_full_version >= '3.7.2'", - "version": "==2.12.12" - }, - "attrs": { - "hashes": [ - "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6", - "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c" - ], - "markers": "python_version >= '3.5'", - "version": "==22.1.0" - }, - "bandit": { - "hashes": [ - "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2", - "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a" - ], - "index": "pypi", - "version": "==1.7.4" - }, - "black": { - "hashes": [ - "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7", - "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6", - "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650", - "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb", - "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d", - "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d", - "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de", - "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395", - "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae", - "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa", - "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef", - "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383", - "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66", - "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87", - "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d", - "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0", - "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b", - "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458", - "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4", - "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1", - "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff" - ], - "index": "pypi", - "version": "==22.10.0" - }, - "boto3": { - "hashes": [ - "sha256:15b059251990706c5d5a556c42a6e6e781a51edadf48a42afdd06bd313adfaf2", - "sha256:27efa5eb229364bc4643d9e182de0891f73c21b65ef80b5bd02a977caeaf595c" - ], - "index": "pypi", - "version": "==1.26.9" - }, - "botocore": { - "hashes": [ - "sha256:01f168e2418419a6d8b335ecc4330faa6a7332d5a097029fc9b4a3ae3c41cea2", - "sha256:c876a7c6a07d7f86f46a8cc765f0d38999b84ac1d7b0666d6ce08c0503e13145" - ], - "index": "pypi", - "version": "==1.29.9" - }, - "certifi": { - "hashes": [ - "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14", - "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==2022.9.24" - }, - "cffi": { - "hashes": [ - "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", - "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", - "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", - "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", - "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", - "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", - "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", - "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", - "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", - "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", - "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", - "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", - "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", - "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", - "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", - "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", - "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", - "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", - "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", - "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", - "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", - "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", - "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", - "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", - "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", - "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", - "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", - "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", - "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", - "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", - "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", - "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", - "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", - "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", - "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", - "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", - "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", - "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", - "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", - "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", - "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", - "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", - "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", - "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", - "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", - "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", - "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", - "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", - "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", - "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", - "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", - "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", - "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", - "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", - "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", - "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", - "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", - "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", - "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", - "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", - "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", - "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", - "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", - "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" - ], - "version": "==1.15.1" - }, - "charset-normalizer": { - "hashes": [ - "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", - "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==2.1.1" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "coverage": { - "hashes": [ - "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79", - "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a", - "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f", - "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a", - "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa", - "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398", - "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba", - "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d", - "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf", - "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b", - "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518", - "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d", - "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795", - "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2", - "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e", - "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32", - "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745", - "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b", - "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e", - "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d", - "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f", - "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660", - "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62", - "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6", - "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04", - "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c", - "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5", - "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef", - "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc", - "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae", - "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578", - "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466", - "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4", - "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91", - "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0", - "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4", - "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b", - "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe", - "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b", - "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75", - "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b", - "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c", - "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72", - "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b", - "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f", - "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e", - "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53", - "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3", - "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84", - "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987" - ], - "index": "pypi", - "version": "==6.5.0" - }, - "cryptography": { - "hashes": [ - "sha256:068147f32fa662c81aebab95c74679b401b12b57494872886eb5c1139250ec5d", - "sha256:06fc3cc7b6f6cca87bd56ec80a580c88f1da5306f505876a71c8cfa7050257dd", - "sha256:25c1d1f19729fb09d42e06b4bf9895212292cb27bb50229f5aa64d039ab29146", - "sha256:402852a0aea73833d982cabb6d0c3bb582c15483d29fb7085ef2c42bfa7e38d7", - "sha256:4e269dcd9b102c5a3d72be3c45d8ce20377b8076a43cbed6f660a1afe365e436", - "sha256:5419a127426084933076132d317911e3c6eb77568a1ce23c3ac1e12d111e61e0", - "sha256:554bec92ee7d1e9d10ded2f7e92a5d70c1f74ba9524947c0ba0c850c7b011828", - "sha256:5e89468fbd2fcd733b5899333bc54d0d06c80e04cd23d8c6f3e0542358c6060b", - "sha256:65535bc550b70bd6271984d9863a37741352b4aad6fb1b3344a54e6950249b55", - "sha256:6ab9516b85bebe7aa83f309bacc5f44a61eeb90d0b4ec125d2d003ce41932d36", - "sha256:6addc3b6d593cd980989261dc1cce38263c76954d758c3c94de51f1e010c9a50", - "sha256:728f2694fa743a996d7784a6194da430f197d5c58e2f4e278612b359f455e4a2", - "sha256:785e4056b5a8b28f05a533fab69febf5004458e20dad7e2e13a3120d8ecec75a", - "sha256:78cf5eefac2b52c10398a42765bfa981ce2372cbc0457e6bf9658f41ec3c41d8", - "sha256:7f836217000342d448e1c9a342e9163149e45d5b5eca76a30e84503a5a96cab0", - "sha256:8d41a46251bf0634e21fac50ffd643216ccecfaf3701a063257fe0b2be1b6548", - "sha256:984fe150f350a3c91e84de405fe49e688aa6092b3525f407a18b9646f6612320", - "sha256:9b24bcff7853ed18a63cfb0c2b008936a9554af24af2fb146e16d8e1aed75748", - "sha256:b1b35d9d3a65542ed2e9d90115dfd16bbc027b3f07ee3304fc83580f26e43249", - "sha256:b1b52c9e5f8aa2b802d48bd693190341fae201ea51c7a167d69fc48b60e8a959", - "sha256:bbf203f1a814007ce24bd4d51362991d5cb90ba0c177a9c08825f2cc304d871f", - "sha256:be243c7e2bfcf6cc4cb350c0d5cdf15ca6383bbcb2a8ef51d3c9411a9d4386f0", - "sha256:bfbe6ee19615b07a98b1d2287d6a6073f734735b49ee45b11324d85efc4d5cbd", - "sha256:c46837ea467ed1efea562bbeb543994c2d1f6e800785bd5a2c98bc096f5cb220", - "sha256:dfb4f4dd568de1b6af9f4cda334adf7d72cf5bc052516e1b2608b683375dd95c", - "sha256:ed7b00096790213e09eb11c97cc6e2b757f15f3d2f85833cd2d3ec3fe37c1722" - ], - "markers": "python_version >= '3.6'", - "version": "==38.0.3" - }, - "dill": { - "hashes": [ - "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0", - "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373" - ], - "markers": "python_version >= '3.7'", - "version": "==0.3.6" - }, - "docker": { - "hashes": [ - "sha256:896c4282e5c7af5c45e8b683b0b0c33932974fe6e50fc6906a0a83616ab3da97", - "sha256:dbcb3bd2fa80dca0788ed908218bf43972772009b881ed1e20dfc29a65e49782" - ], - "index": "pypi", - "version": "==6.0.1" - }, - "dparse": { - "hashes": [ - "sha256:8097076f1dd26c377f30d4745e6ec18fef42f3bf493933b842ac5bafad8c345f", - "sha256:d45255bda21f998bc7ddf2afd5e62505ba6134756ba2d42a84c56b0826614dfe" - ], - "markers": "python_version >= '3.5'", - "version": "==0.6.2" - }, - "exceptiongroup": { - "hashes": [ - "sha256:6002703c7d31fb9950ddc8780840f67880c440895dc1151dd551553aa1246e4a", - "sha256:76cac74b5207c5997678a1c7105cb6f14213c9c63c096a38cfcb529d83ce5c02" - ], - "markers": "python_version < '3.11'", - "version": "==1.0.3" - }, - "execnet": { - "hashes": [ - "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5", - "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.9.0" - }, - "flake8": { - "hashes": [ - "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db", - "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248" - ], - "index": "pypi", - "version": "==5.0.4" - }, - "freezegun": { - "hashes": [ - "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446", - "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f" - ], - "index": "pypi", - "version": "==1.2.2" - }, - "gitdb": { - "hashes": [ - "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd", - "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa" - ], - "markers": "python_version >= '3.6'", - "version": "==4.0.9" - }, - "gitpython": { - "hashes": [ - "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f", - "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.29" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "importlib-resources": { - "hashes": [ - "sha256:c01b1b94210d9849f286b86bb51bcea7cd56dde0600d8db721d7b81330711668", - "sha256:ee17ec648f85480d523596ce49eae8ead87d5631ae1551f913c0100b5edd3437" - ], - "markers": "python_version >= '3.7'", - "version": "==5.10.0" - }, - "iniconfig": { - "hashes": [ - "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", - "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" - ], - "version": "==1.1.1" - }, - "isort": { - "hashes": [ - "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7", - "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951" - ], - "markers": "python_full_version >= '3.6.1' and python_full_version < '4.0.0'", - "version": "==5.10.1" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.2" - }, - "jmespath": { - "hashes": [ - "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", - "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" - ], - "markers": "python_version >= '3.7'", - "version": "==1.0.1" - }, - "jsonschema": { - "hashes": [ - "sha256:5bfcf2bca16a087ade17e02b282d34af7ccd749ef76241e7f9bd7c0cb8a9424d", - "sha256:f660066c3966db7d6daeaea8a75e0b68237a48e51cf49882087757bb59916248" - ], - "markers": "python_version >= '3.7'", - "version": "==4.17.0" - }, - "jsonschema-spec": { - "hashes": [ - "sha256:1e525177574c23ae0f55cd62382632a083a0339928f0ca846a975a4da9851cec", - "sha256:780a22d517cdc857d9714a80d8349c546945063f20853ea32ba7f85bc643ec7d" - ], - "markers": "python_version >= '3.7' and python_full_version < '4.0.0'", - "version": "==0.1.2" - }, - "lazy-object-proxy": { - "hashes": [ - "sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada", - "sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d", - "sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7", - "sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe", - "sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd", - "sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c", - "sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858", - "sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288", - "sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec", - "sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f", - "sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891", - "sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c", - "sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25", - "sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156", - "sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8", - "sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f", - "sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e", - "sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0", - "sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b" - ], - "markers": "python_version >= '3.7'", - "version": "==1.8.0" - }, - "markupsafe": { - "hashes": [ - "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", - "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", - "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", - "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", - "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", - "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", - "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", - "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", - "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", - "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", - "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", - "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", - "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", - "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", - "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", - "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", - "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", - "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", - "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", - "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", - "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", - "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", - "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", - "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", - "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", - "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", - "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", - "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", - "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", - "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", - "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", - "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", - "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", - "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", - "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", - "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", - "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", - "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", - "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", - "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.1" - }, - "mccabe": { - "hashes": [ - "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", - "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" - ], - "markers": "python_version >= '3.6'", - "version": "==0.7.0" - }, - "mock": { - "hashes": [ - "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62", - "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc" - ], - "markers": "python_version >= '3.6'", - "version": "==4.0.3" - }, - "moto": { - "hashes": [ - "sha256:2fb909d2ea1b732f89604e4268e2c2207c253e590a635a410c3c2aaebb34e113", - "sha256:ba03b638cf3b1cec64cbe9ac0d184ca898b69020c8e3c5b9b4961c1670629010" - ], - "index": "pypi", - "version": "==4.0.9" - }, - "mypy-extensions": { - "hashes": [ - "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", - "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" - ], - "version": "==0.4.3" - }, - "openapi-schema-validator": { - "hashes": [ - "sha256:34fbd14b7501abe25e64d7b4624a9db02cde1a578d285b3da6f34b290cdf0b3a", - "sha256:7cf27585dd7970b7257cefe48e1a3a10d4e34421831bdb472d96967433bc27bd" - ], - "markers": "python_version >= '3.7' and python_full_version < '4.0.0'", - "version": "==0.3.4" - }, - "openapi-spec-validator": { - "hashes": [ - "sha256:4a8aee1e45b1ac868e07ab25e18828fe9837baddd29a8e20fdb3d3c61c8eea3d", - "sha256:8248634bad1f23cac5d5a34e193ab36e23914057ca69e91a1ede5af75552c465" - ], - "index": "pypi", - "version": "==0.5.1" - }, - "packaging": { - "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" - ], - "markers": "python_version >= '3.6'", - "version": "==21.3" - }, - "pathable": { - "hashes": [ - "sha256:5c869d315be50776cc8a993f3af43e0c60dc01506b399643f919034ebf4cdcab", - "sha256:cdd7b1f9d7d5c8b8d3315dbf5a86b2596053ae845f056f57d97c0eefff84da14" - ], - "markers": "python_version >= '3.7' and python_full_version < '4.0.0'", - "version": "==0.4.3" - }, - "pathspec": { - "hashes": [ - "sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5", - "sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0" - ], - "markers": "python_version >= '3.7'", - "version": "==0.10.2" - }, - "pbr": { - "hashes": [ - "sha256:b97bc6695b2aff02144133c2e7399d5885223d42b7912ffaec2ca3898e673bfe", - "sha256:db2317ff07c84c4c63648c9064a79fe9d9f5c7ce85a9099d4b6258b3db83225a" - ], - "markers": "python_version >= '2.6'", - "version": "==5.11.0" - }, - "platformdirs": { - "hashes": [ - "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7", - "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10" - ], - "markers": "python_version >= '3.7'", - "version": "==2.5.4" - }, - "pluggy": { - "hashes": [ - "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", - "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" - }, - "pycodestyle": { - "hashes": [ - "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785", - "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b" - ], - "markers": "python_version >= '3.6'", - "version": "==2.9.1" - }, - "pycparser": { - "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" - ], - "version": "==2.21" - }, - "pyflakes": { - "hashes": [ - "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2", - "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3" - ], - "markers": "python_version >= '3.6'", - "version": "==2.5.0" - }, - "pylint": { - "hashes": [ - "sha256:3b120505e5af1d06a5ad76b55d8660d44bf0f2fc3c59c2bdd94e39188ee3a4df", - "sha256:c2108037eb074334d9e874dc3c783752cc03d0796c88c9a9af282d0f161a1004" - ], - "index": "pypi", - "version": "==2.15.5" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" - }, - "pyrsistent": { - "hashes": [ - "sha256:055ab45d5911d7cae397dc418808d8802fb95262751872c841c170b0dbf51eed", - "sha256:111156137b2e71f3a9936baf27cb322e8024dac3dc54ec7fb9f0bcf3249e68bb", - "sha256:187d5730b0507d9285a96fca9716310d572e5464cadd19f22b63a6976254d77a", - "sha256:21455e2b16000440e896ab99e8304617151981ed40c29e9507ef1c2e4314ee95", - "sha256:2aede922a488861de0ad00c7630a6e2d57e8023e4be72d9d7147a9fcd2d30712", - "sha256:3ba4134a3ff0fc7ad225b6b457d1309f4698108fb6b35532d015dca8f5abed73", - "sha256:456cb30ca8bff00596519f2c53e42c245c09e1a4543945703acd4312949bfd41", - "sha256:71d332b0320642b3261e9fee47ab9e65872c2bd90260e5d225dabeed93cbd42b", - "sha256:879b4c2f4d41585c42df4d7654ddffff1239dc4065bc88b745f0341828b83e78", - "sha256:9cd3e9978d12b5d99cbdc727a3022da0430ad007dacf33d0bf554b96427f33ab", - "sha256:a178209e2df710e3f142cbd05313ba0c5ebed0a55d78d9945ac7a4e09d923308", - "sha256:b39725209e06759217d1ac5fcdb510e98670af9e37223985f330b611f62e7425", - "sha256:bfa0351be89c9fcbcb8c9879b826f4353be10f58f8a677efab0c017bf7137ec2", - "sha256:bfd880614c6237243ff53a0539f1cb26987a6dc8ac6e66e0c5a40617296a045e", - "sha256:c43bec251bbd10e3cb58ced80609c5c1eb238da9ca78b964aea410fb820d00d6", - "sha256:d690b18ac4b3e3cab73b0b7aa7dbe65978a172ff94970ff98d82f2031f8971c2", - "sha256:d6982b5a0237e1b7d876b60265564648a69b14017f3b5f908c5be2de3f9abb7a", - "sha256:dec3eac7549869365fe263831f576c8457f6c833937c68542d08fde73457d291", - "sha256:e371b844cec09d8dc424d940e54bba8f67a03ebea20ff7b7b0d56f526c71d584", - "sha256:e5d8f84d81e3729c3b506657dddfe46e8ba9c330bf1858ee33108f8bb2adb38a", - "sha256:ea6b79a02a28550c98b6ca9c35b9f492beaa54d7c5c9e9949555893c8a9234d0", - "sha256:f1258f4e6c42ad0b20f9cfcc3ada5bd6b83374516cd01c0960e3cb75fdca6770" - ], - "markers": "python_version >= '3.7'", - "version": "==0.19.2" - }, - "pytest": { - "hashes": [ - "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71", - "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59" - ], - "index": "pypi", - "version": "==7.2.0" - }, - "pytest-xdist": { - "hashes": [ - "sha256:688da9b814370e891ba5de650c9327d1a9d861721a524eb917e620eec3e90291", - "sha256:9feb9a18e1790696ea23e1434fa73b325ed4998b0e9fcb221f16fd1945e6df1b" - ], - "index": "pypi", - "version": "==3.0.2" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "pytz": { - "hashes": [ - "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427", - "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2" - ], - "version": "==2022.6" - }, - "pyyaml": { - "hashes": [ - "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==6.0" - }, - "requests": { - "hashes": [ - "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", - "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" - ], - "markers": "python_version >= '3.7' and python_version < '4'", - "version": "==2.28.1" - }, - "responses": { - "hashes": [ - "sha256:396acb2a13d25297789a5866b4881cf4e46ffd49cc26c43ab1117f40b973102e", - "sha256:dcf294d204d14c436fddcc74caefdbc5764795a40ff4e6a7740ed8ddbf3294be" - ], - "markers": "python_version >= '3.7'", - "version": "==0.22.0" - }, - "ruamel.yaml": { - "hashes": [ - "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7", - "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af" - ], - "markers": "python_version >= '3'", - "version": "==0.17.21" - }, - "ruamel.yaml.clib": { - "hashes": [ - "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e", - "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3", - "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5", - "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497", - "sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f", - "sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac", - "sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697", - "sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763", - "sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282", - "sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1", - "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072", - "sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9", - "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5", - "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231", - "sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93", - "sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b", - "sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb", - "sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f", - "sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307", - "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8", - "sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b", - "sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b", - "sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640", - "sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7", - "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a", - "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71", - "sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8", - "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7", - "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80", - "sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e", - "sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab", - "sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0", - "sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646" - ], - "markers": "python_version < '3.11' and platform_python_implementation == 'CPython'", - "version": "==0.2.7" - }, - "s3transfer": { - "hashes": [ - "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd", - "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947" - ], - "markers": "python_version >= '3.7'", - "version": "==0.6.0" - }, - "safety": { - "hashes": [ - "sha256:6e6fcb7d4e8321098cf289f59b65051cafd3467f089c6e57c9f894ae32c23b71", - "sha256:8f098d12b607db2756886280e85c28ece8db1bba4f45fc5f981f4663217bd619" - ], - "index": "pypi", - "version": "==2.3.1" - }, - "setuptools": { - "hashes": [ - "sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31", - "sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f" - ], - "markers": "python_version >= '3.7'", - "version": "==65.5.1" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "smmap": { - "hashes": [ - "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94", - "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936" - ], - "markers": "python_version >= '3.6'", - "version": "==5.0.0" - }, - "stevedore": { - "hashes": [ - "sha256:7f8aeb6e3f90f96832c301bff21a7eb5eefbe894c88c506483d355565d88cc1a", - "sha256:aa6436565c069b2946fe4ebff07f5041e0c8bf18c7376dd29edf80cf7d524e4e" - ], - "markers": "python_version >= '3.8'", - "version": "==4.1.1" - }, - "sure": { - "hashes": [ - "sha256:34ae88c846046742ef074036bf311dc90ab152b7bc09c342b281cebf676727a2" - ], - "index": "pypi", - "version": "==2.0.0" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_full_version < '3.11.0a7'", - "version": "==2.0.1" - }, - "tomlkit": { - "hashes": [ - "sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b", - "sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73" - ], - "markers": "python_version >= '3.6'", - "version": "==0.11.6" - }, - "types-toml": { - "hashes": [ - "sha256:171bdb3163d79a520560f24ba916a9fc9bff81659c5448a9fea89240923722be", - "sha256:b7b5c4977f96ab7b5ac06d8a6590d17c0bf252a96efc03b109c2711fb3e0eafd" - ], - "version": "==0.10.8.1" - }, - "typing-extensions": { - "hashes": [ - "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa", - "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e" - ], - "markers": "python_version >= '3.7'", - "version": "==4.4.0" - }, - "urllib3": { - "hashes": [ - "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", - "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", - "version": "==1.26.12" - }, - "vulture": { - "hashes": [ - "sha256:2515fa848181001dc8a73aba6a01a1a17406f5d372f24ec7f7191866f9f4997e", - "sha256:e792e903ccc063ec4873a8979dcf11b51ea3d65a2d3b31c113d47be48f0cdcae" - ], - "index": "pypi", - "version": "==2.6" - }, - "websocket-client": { - "hashes": [ - "sha256:d6b06432f184438d99ac1f456eaf22fe1ade524c3dd16e661142dc54e9cba574", - "sha256:d6e8f90ca8e2dd4e8027c4561adeb9456b54044312dba655e7cae652ceb9ae59" - ], - "markers": "python_version >= '3.7'", - "version": "==1.4.2" - }, - "werkzeug": { - "hashes": [ - "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", - "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5" - ], - "markers": "python_version >= '3.7'", - "version": "==2.2.2" - }, - "wrapt": { - "hashes": [ - "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3", - "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b", - "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4", - "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2", - "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656", - "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3", - "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff", - "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310", - "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a", - "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57", - "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069", - "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383", - "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe", - "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87", - "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d", - "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b", - "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907", - "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f", - "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0", - "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28", - "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1", - "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853", - "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc", - "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3", - "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3", - "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164", - "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1", - "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c", - "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1", - "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7", - "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1", - "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320", - "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed", - "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1", - "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248", - "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c", - "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456", - "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77", - "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef", - "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1", - "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7", - "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86", - "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4", - "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d", - "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d", - "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8", - "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5", - "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471", - "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00", - "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68", - "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3", - "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d", - "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735", - "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d", - "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569", - "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7", - "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59", - "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5", - "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb", - "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b", - "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f", - "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462", - "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015", - "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af" - ], - "markers": "python_version < '3.11'", - "version": "==1.14.1" - }, - "xmltodict": { - "hashes": [ - "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56", - "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852" - ], - "markers": "python_version >= '3.4'", - "version": "==0.13.0" - }, - "zipp": { - "hashes": [ - "sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1", - "sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8" - ], - "markers": "python_version < '3.10'", - "version": "==3.10.0" - } - } } diff --git a/providers/aws/config.yaml b/providers/aws/config.yaml index 1add8a98..7e910979 100644 --- a/providers/aws/config.yaml +++ b/providers/aws/config.yaml @@ -5,7 +5,7 @@ shodan_api_key: null # Single account environment: No action required. The AWS account number will be automatically added by the checks. # Multi account environment: Any additional trusted account number should be added as a space separated list, e.g. # trusted_account_ids : ["123456789012", "098765432109", "678901234567"] -trusted_account_ids : [] +trusted_account_ids: [] # cloudwatch_log_group_retention_policy_specific_days_enabled --> by default is 365 days log_group_retention_days: 365 @@ -20,3 +20,20 @@ max_ec2_instance_age_in_days: 180 max_idle_disconnect_timeout_in_seconds: 600 # 10 Minutes max_disconnect_timeout_in_seconds: 300 # 5 Minutes max_session_duration_seconds: 36000 # 10 Hours + +# Lambda Configuration +obsolete_lambda_runtimes: + [ + "python3.6", + "python2.7", + "nodejs4.3", + "nodejs4.3-edge", + "nodejs6.10", + "nodejs", + "nodejs8.10", + "nodejs10.x", + "dotnetcore1.0", + "dotnetcore2.0", + "dotnetcore2.1", + "ruby2.5", + ] diff --git a/providers/aws/services/awslambda/__init__.py b/providers/aws/services/awslambda/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/awslambda/awslambda_client.py b/providers/aws/services/awslambda/awslambda_client.py new file mode 100644 index 00000000..d8e6c325 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_client.py @@ -0,0 +1,4 @@ +from providers.aws.lib.audit_info.audit_info import current_audit_info +from providers.aws.services.awslambda.awslambda_service import Lambda + +awslambda_client = Lambda(current_audit_info) diff --git a/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/__init__.py b/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.metadata.json b/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.metadata.json new file mode 100644 index 00000000..e9983eef --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "awslambda_function_invoke_api_operations_cloudtrail_logging_enabled", + "CheckTitle": "Check if Lambda functions invoke API operations are being recorded by CloudTrail.", + "CheckType": [], + "ServiceName": "lambda", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name", + "Severity": "low", + "ResourceType": "AwsLambdaFunction", + "Description": "Check if Lambda functions invoke API operations are being recorded by CloudTrail.", + "Risk": "If logs are not enabled; monitoring of service use and threat analysis is not possible.", + "RelatedUrl": "https://docs.aws.amazon.com/lambda/latest/dg/logging-using-cloudtrail.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Make sure you are logging information about Lambda operations. Create a lifecycle and use cases for each trail.", + "Url": "https://docs.aws.amazon.com/lambda/latest/dg/logging-using-cloudtrail.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.py b/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.py new file mode 100644 index 00000000..7725482c --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.py @@ -0,0 +1,38 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.awslambda.awslambda_client import awslambda_client +from providers.aws.services.cloudtrail.cloudtrail_client import cloudtrail_client + + +class awslambda_function_invoke_api_operations_cloudtrail_logging_enabled(Check): + def execute(self): + findings = [] + for function in awslambda_client.functions.values(): + report = Check_Report(self.metadata) + report.region = function.region + report.resource_id = function.name + report.resource_arn = function.arn + + report.status = "FAIL" + report.status_extended = ( + f"Lambda function {function.name} is not recorded by CloudTrail" + ) + lambda_recorded_cloudtrail = False + for trail in cloudtrail_client.trails: + for data_event in trail.data_events: + for resource in data_event["DataResources"]: + if ( + resource["Type"] == "AWS::Lambda::Function" + and function.arn in resource["Values"] + ): + lambda_recorded_cloudtrail = True + break + + if lambda_recorded_cloudtrail: + break + if lambda_recorded_cloudtrail: + report.status = "PASS" + report.status_extended = f"Lambda function {function.name} is recorded by CloudTrail {trail.name}" + break + findings.append(report) + + return findings diff --git a/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled_test.py b/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled_test.py new file mode 100644 index 00000000..85091e52 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled_test.py @@ -0,0 +1,211 @@ +# from datetime import datetime +# from unittest import mock + +# from boto3 import session +# from moto.core import DEFAULT_ACCOUNT_ID + +# from providers.aws.lib.audit_info.audit_info import AWS_Audit_Info +# from providers.aws.services.awslambda.awslambda_service import Function +# from providers.aws.services.cloudtrail.cloudtrail_service import Trail + +# AWS_REGION = "us-east-1" + + +# class Test_awslambda_function_invoke_api_operations_cloudtrail_logging_enabled: +# # Mocked Audit Info +# def set_mocked_audit_info(self): +# audit_info = AWS_Audit_Info( +# original_session=None, +# audit_session=session.Session( +# profile_name=None, +# botocore_session=None, +# ), +# audited_account=None, +# audited_user_id=None, +# audited_partition="aws", +# audited_identity_arn=None, +# profile=None, +# profile_region=None, +# credentials=None, +# assumed_role_info=None, +# audited_regions=None, +# organizations_metadata=None, +# ) +# return audit_info + +# def test_no_functions(self): +# lambda_client = mock.MagicMock +# lambda_client.functions = {} +# cloudtrail_client = mock.MagicMock +# cloudtrail_client.trails = [] + +# with mock.patch( +# "providers.aws.services.awslambda.awslambda_service.Lambda", +# new=lambda_client, +# ), mock.patch( +# "providers.aws.lib.audit_info.audit_info.current_audit_info", +# self.set_mocked_audit_info(), +# ), mock.patch( +# "providers.aws.services.cloudtrail.cloudtrail_service.Cloudtrail", +# new=cloudtrail_client, +# ): +# # Test Check +# from providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled import ( +# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled, +# ) + +# check = ( +# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled() +# ) +# result = check.execute() + +# assert len(result) == 0 + +# def test_lambda_not_recorded_by_cloudtrail(self): +# # Lambda Client +# lambda_client = mock.MagicMock +# function_name = "test-lambda" +# function_runtime = "python3.9" +# function_arn = ( +# f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" +# ) +# lambda_client.functions = { +# function_name: Function( +# name=function_name, +# arn=function_arn, +# region=AWS_REGION, +# runtime=function_runtime, +# ) +# } +# # CloudTrail Client +# cloudtrail_client = mock.MagicMock +# cloudtrail_client.trails = [ +# Trail( +# name="test-trail", +# is_multiregion=False, +# home_region=AWS_REGION, +# arn="", +# region=AWS_REGION, +# is_logging=True, +# log_file_validation_enabled=True, +# latest_cloudwatch_delivery_time=datetime(2022, 1, 1), +# s3_bucket="", +# kms_key="", +# log_group_arn="", +# data_events=[ +# { +# "ReadWriteType": "All", +# "IncludeManagementEvents": True, +# "DataResources": [], +# "ExcludeManagementEventSources": [], +# } +# ], +# ) +# ] + +# with mock.patch( +# "providers.aws.services.awslambda.awslambda_service.Lambda", +# new=lambda_client, +# ), mock.patch( +# "providers.aws.services.cloudtrail.cloudtrail_service.Cloudtrail", +# new=cloudtrail_client, +# ): + +# # Test Check +# from providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled import ( +# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled, +# ) + +# check = ( +# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled() +# ) +# result = check.execute() + +# assert len(result) == 1 +# assert result[0].region == AWS_REGION +# assert result[0].resource_id == function_name +# assert result[0].resource_arn == function_arn +# assert result[0].status == "FAIL" +# assert ( +# result[0].status_extended +# == f"Lambda function {function_name} is not recorded by CloudTrail" +# ) + +# def test_lambda_recorded_by_cloudtrail(self): +# # Lambda Client +# lambda_client = mock.MagicMock +# function_name = "test-lambda" +# function_runtime = "python3.9" +# function_arn = ( +# f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" +# ) +# lambda_client.functions = { +# function_name: Function( +# name=function_name, +# arn=function_arn, +# region=AWS_REGION, +# runtime=function_runtime, +# ) +# } +# # CloudTrail Client +# cloudtrail_client = mock.MagicMock +# trail_name = "test-trail" +# cloudtrail_client.trails = [ +# Trail( +# name=trail_name, +# is_multiregion=False, +# home_region=AWS_REGION, +# arn="", +# region=AWS_REGION, +# is_logging=True, +# log_file_validation_enabled=True, +# latest_cloudwatch_delivery_time=datetime(2022, 1, 1), +# s3_bucket="", +# kms_key="", +# log_group_arn="", +# data_events=[ +# { +# "ReadWriteType": "All", +# "IncludeManagementEvents": True, +# "DataResources": [ +# { +# "Type": "AWS::Lambda::Function", +# "Values": [ +# function_arn, +# ], +# }, +# ], +# "ExcludeManagementEventSources": [], +# } +# ], +# ) +# ] + +# with mock.patch( +# "providers.aws.services.awslambda.awslambda_service.Lambda", +# new=lambda_client, +# ), mock.patch( +# "providers.aws.services.cloudtrail.cloudtrail_service.Cloudtrail", +# new=cloudtrail_client, +# ): + +# # +# # Test Check +# from providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled import ( +# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled, +# ) + +# check = ( +# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled() +# ) +# result = check.execute() + +# assert len(result) == 1 +# assert result[0].region == AWS_REGION +# assert result[0].resource_id == function_name +# assert result[0].resource_arn == function_arn +# assert result[0].status == "PASS" +# assert ( +# result[0].status_extended +# == f"Lambda function {function_name} is recorded by CloudTrail {trail_name}" +# ) diff --git a/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/__init__.py b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code.metadata.json b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code.metadata.json new file mode 100644 index 00000000..d08368ac --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "awslambda_function_no_secrets_in_code", + "CheckTitle": "Find secrets in Lambda functions code.", + "CheckType": [], + "ServiceName": "lambda", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name", + "Severity": "critical", + "ResourceType": "AwsLambdaFunction", + "Description": "Find secrets in Lambda functions code.", + "Risk": "The use of a hard-coded password increases the possibility of password guessing. If hard-coded passwords are used; it is possible that malicious users gain access through the account in question.", + "RelatedUrl": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Use Secrets Manager to securely provide database credentials to Lambda functions and secure the databases as well as use the credentials to connect and query them without hardcoding the secrets in code or passing them through environmental variables.", + "Url": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code.py b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code.py new file mode 100644 index 00000000..fc7fb8c3 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code.py @@ -0,0 +1,40 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.awslambda.awslambda_client import awslambda_client +import tempfile +import os +from detect_secrets import SecretsCollection +from detect_secrets.settings import default_settings + + +class awslambda_function_no_secrets_in_code(Check): + def execute(self): + findings = [] + for function in awslambda_client.functions.values(): + report = Check_Report(self.metadata) + report.region = function.region + report.resource_id = function.name + report.resource_arn = function.arn + + report.status = "PASS" + report.status_extended = ( + f"No secrets found in Lambda function {function.name} code" + ) + + with tempfile.TemporaryDirectory() as tmp_dir_name: + function.code.code_zip.extractall(tmp_dir_name) + # List all files + files_in_zip = next(os.walk(tmp_dir_name))[2] + for file in files_in_zip: + + secrets = SecretsCollection() + with default_settings(): + secrets.scan_file(f"{tmp_dir_name}/{file}") + + if secrets.json(): + report.status = "FAIL" + report.status_extended = f"Potential secret found in Lambda function {function.name} code" + break + + findings.append(report) + + return findings diff --git a/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code_test.py b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code_test.py new file mode 100644 index 00000000..ee994d4f --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_code/awslambda_function_no_secrets_in_code_test.py @@ -0,0 +1,124 @@ +import zipfile +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.awslambda.awslambda_service import Function, LambdaCode +from providers.aws.services.awslambda.awslambda_service_test import create_zip_file + +AWS_REGION = "us-east-1" + + +class Test_awslambda_function_no_secrets_in_code: + def test_no_functions(self): + lambda_client = mock.MagicMock + lambda_client.functions = {} + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code import ( + awslambda_function_no_secrets_in_code, + ) + + check = awslambda_function_no_secrets_in_code() + result = check.execute() + + assert len(result) == 0 + + def test_function_code_with_secrets(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + code_with_secrets = """ + def lambda_handler(event, context): + db_password = "test-password" + print("custom log event") + return event + """ + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + code=LambdaCode( + location="", + code_zip=zipfile.ZipFile(create_zip_file(code_with_secrets)), + ), + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code import ( + awslambda_function_no_secrets_in_code, + ) + + check = awslambda_function_no_secrets_in_code() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Potential secret found in Lambda function {function_name} code" + ) + + def test_function_code_without_secrets(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + code_with_secrets = """ + def lambda_handler(event, context): + print("custom log event") + return event + """ + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + code=LambdaCode( + location="", + code_zip=zipfile.ZipFile(create_zip_file(code_with_secrets)), + ), + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_no_secrets_in_code.awslambda_function_no_secrets_in_code import ( + awslambda_function_no_secrets_in_code, + ) + + check = awslambda_function_no_secrets_in_code() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"No secrets found in Lambda function {function_name} code" + ) diff --git a/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/__init__.py b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables.metadata.json b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables.metadata.json new file mode 100644 index 00000000..1f7c2a9d --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "awslambda_function_no_secrets_in_variables", + "CheckTitle": "Find secrets in Lambda functions variables.", + "CheckType": [], + "ServiceName": "lambda", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name", + "Severity": "critical", + "ResourceType": "AwsLambdaFunction", + "Description": "Find secrets in Lambda functions variables.", + "Risk": "The use of a hard-coded password increases the possibility of password guessing. If hard-coded passwords are used; it is possible that malicious users gain access through the account in question.", + "RelatedUrl": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html", + "Remediation": { + "Code": { + "CLI": "https://docs.bridgecrew.io/docs/bc_aws_secrets_3#cli-command", + "NativeIaC": "https://docs.bridgecrew.io/docs/bc_aws_secrets_3#cloudformation", + "Other": "", + "Terraform": "https://docs.bridgecrew.io/docs/bc_aws_secrets_3#terraform" + }, + "Recommendation": { + "Text": "Use Secrets Manager to securely provide database credentials to Lambda functions and secure the databases as well as use the credentials to connect and query them without hardcoding the secrets in code or passing them through environmental variables.", + "Url": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables.py b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables.py new file mode 100644 index 00000000..404f3996 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables.py @@ -0,0 +1,44 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.awslambda.awslambda_client import awslambda_client +import tempfile +import json +from detect_secrets import SecretsCollection +from detect_secrets.settings import default_settings +import os + + +class awslambda_function_no_secrets_in_variables(Check): + def execute(self): + findings = [] + for function in awslambda_client.functions.values(): + report = Check_Report(self.metadata) + report.region = function.region + report.resource_id = function.name + report.resource_arn = function.arn + + report.status = "PASS" + report.status_extended = ( + f"No secrets found in Lambda function {function.name} variables" + ) + + if function.environment: + temp_env_data_file = tempfile.NamedTemporaryFile(delete=False) + temp_env_data_file.write( + bytes( + json.dumps(function.environment), encoding="raw_unicode_escape" + ) + ) + temp_env_data_file.close() + secrets = SecretsCollection() + with default_settings(): + secrets.scan_file(temp_env_data_file.name) + + if secrets.json(): + report.status = "FAIL" + report.status_extended = f"Potential secret found in Lambda function {function.name} variables" + + os.remove(temp_env_data_file.name) + + findings.append(report) + + return findings diff --git a/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables_test.py b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables_test.py new file mode 100644 index 00000000..40e4139f --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_no_secrets_in_variables/awslambda_function_no_secrets_in_variables_test.py @@ -0,0 +1,146 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.awslambda.awslambda_service import Function + +AWS_REGION = "us-east-1" + + +class Test_awslambda_function_no_secrets_in_variables: + def test_no_functions(self): + lambda_client = mock.MagicMock + lambda_client.functions = {} + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_no_secrets_in_variables.awslambda_function_no_secrets_in_variables import ( + awslambda_function_no_secrets_in_variables, + ) + + check = awslambda_function_no_secrets_in_variables() + result = check.execute() + + assert len(result) == 0 + + def test_function_no_variables(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_no_secrets_in_variables.awslambda_function_no_secrets_in_variables import ( + awslambda_function_no_secrets_in_variables, + ) + + check = awslambda_function_no_secrets_in_variables() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"No secrets found in Lambda function {function_name} variables" + ) + + def test_function_secrets_in_variables(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + environment={"db_password": "test-password"}, + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_no_secrets_in_variables.awslambda_function_no_secrets_in_variables import ( + awslambda_function_no_secrets_in_variables, + ) + + check = awslambda_function_no_secrets_in_variables() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Potential secret found in Lambda function {function_name} variables" + ) + + def test_function_no_secrets_in_variables(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + environment={"db_username": "test-user"}, + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_no_secrets_in_variables.awslambda_function_no_secrets_in_variables import ( + awslambda_function_no_secrets_in_variables, + ) + + check = awslambda_function_no_secrets_in_variables() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"No secrets found in Lambda function {function_name} variables" + ) diff --git a/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/__init__.py b/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json b/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json new file mode 100644 index 00000000..058142b6 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "awslambda_function_not_publicly_accessible", + "CheckTitle": "heck if Lambda functions have resource-based policy set as Public.", + "CheckType": [], + "ServiceName": "lambda", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name", + "Severity": "critical", + "ResourceType": "AwsLambdaFunction", + "Description": "Check if Lambda functions have resource-based policy set as Public.", + "Risk": "Publicly accessible services could expose sensitive data to bad actors.", + "RelatedUrl": "https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html", + "Remediation": { + "Code": { + "CLI": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Lambda/function-exposed.html", + "NativeIaC": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Lambda/function-exposed.html", + "Other": "", + "Terraform": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Lambda/function-exposed.html" + }, + "Recommendation": { + "Text": "Grant usage permission on a per-resource basis and applying least privilege principle.", + "Url": "https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py b/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py new file mode 100644 index 00000000..37eed32c --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible.py @@ -0,0 +1,42 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.awslambda.awslambda_client import awslambda_client + + +class awslambda_function_not_publicly_accessible(Check): + def execute(self): + findings = [] + for function in awslambda_client.functions.values(): + report = Check_Report(self.metadata) + report.region = function.region + report.resource_id = function.name + report.resource_arn = function.arn + + report.status = "PASS" + report.status_extended = f"Lambda function {function.name} has a policy resource-based policy not public" + + public_access = False + if function.policy: + for statement in function.policy["Statement"]: + # Only check allow statements + if statement["Effect"] == "Allow": + if ( + "*" in statement["Principal"] + or ( + "AWS" in statement["Principal"] + and "*" in statement["Principal"]["AWS"] + ) + or ( + "CanonicalUser" in statement["Principal"] + and "*" in statement["Principal"]["CanonicalUser"] + ) + ): + public_access = True + break + + if public_access: + report.status = "FAIL" + report.status_extended = f"Lambda function {function.name} has a policy resource-based policy with public access" + + findings.append(report) + + return findings diff --git a/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py b/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py new file mode 100644 index 00000000..a606f093 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_not_publicly_accessible/awslambda_function_not_publicly_accessible_test.py @@ -0,0 +1,189 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.awslambda.awslambda_service import Function + +AWS_REGION = "us-east-1" + + +class Test_awslambda_function_not_publicly_accessible: + def test_no_functions(self): + lambda_client = mock.MagicMock + lambda_client.functions = {} + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( + awslambda_function_not_publicly_accessible, + ) + + check = awslambda_function_not_publicly_accessible() + result = check.execute() + + assert len(result) == 0 + + def test_function_public(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + lambda_policy = { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "public-access", + "Principal": {"AWS": ["*", DEFAULT_ACCOUNT_ID]}, + "Effect": "Allow", + "Action": [ + "lambda:InvokeFunction", + ], + "Resource": [function_arn], + } + ], + } + + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + policy=lambda_policy, + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( + awslambda_function_not_publicly_accessible, + ) + + check = awslambda_function_not_publicly_accessible() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Lambda function {function_name} has a policy resource-based policy with public access" + ) + + def test_function_not_public(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + lambda_policy = { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "public-access", + "Principal": {"AWS": [DEFAULT_ACCOUNT_ID]}, + "Effect": "Allow", + "Action": [ + "lambda:InvokeFunction", + ], + "Resource": [function_arn], + } + ], + } + + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + policy=lambda_policy, + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( + awslambda_function_not_publicly_accessible, + ) + + check = awslambda_function_not_publicly_accessible() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Lambda function {function_name} has a policy resource-based policy not public" + ) + + def test_function_public_with_canonical(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + lambda_policy = { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "public-access", + "Principal": {"CanonicalUser": ["*"]}, + "Effect": "Allow", + "Action": [ + "lambda:InvokeFunction", + ], + "Resource": [function_arn], + } + ], + } + + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + policy=lambda_policy, + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_not_publicly_accessible.awslambda_function_not_publicly_accessible import ( + awslambda_function_not_publicly_accessible, + ) + + check = awslambda_function_not_publicly_accessible() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Lambda function {function_name} has a policy resource-based policy with public access" + ) diff --git a/providers/aws/services/awslambda/awslambda_function_url_cors_policy/__init__.py b/providers/aws/services/awslambda/awslambda_function_url_cors_policy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy.metadata.json b/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy.metadata.json new file mode 100644 index 00000000..a93bfcee --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "awslambda_function_url_cors_policy", + "CheckTitle": "Check Lambda Function URL CORS configuration.", + "CheckType": [], + "ServiceName": "lambda", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name", + "Severity": "medium", + "ResourceType": "AwsLambdaFunction", + "Description": "Check Lambda Function URL CORS configuration.", + "Risk": "Publicly accessible services could expose sensitive data to bad actors.", + "RelatedUrl": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html", + "Remediation": { + "Code": { + "CLI": "aws lambda update-function-url-config --region AWS_REGION --function-name FUNCTION-NAME --auth-type AWS_IAM --cors 'AllowOrigins=https://www.example.com,AllowMethods=*,ExposeHeaders=keep-alive,MaxAge=3600,AllowCredentials=false'", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Grant usage permission on a per-resource basis and applying least privilege principle.", + "Url": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy.py b/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy.py new file mode 100644 index 00000000..3d71907a --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy.py @@ -0,0 +1,23 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.awslambda.awslambda_client import awslambda_client + + +class awslambda_function_url_cors_policy(Check): + def execute(self): + findings = [] + for function in awslambda_client.functions.values(): + report = Check_Report(self.metadata) + report.region = function.region + report.resource_id = function.name + report.resource_arn = function.arn + if function.url_config: + if "*" in function.url_config.cors_config.allow_origins: + report.status = "FAIL" + report.status_extended = f"Lambda function {function.name} URL has a wide CORS configuration" + else: + report.status = "PASS" + report.status_extended = f"Lambda function {function.name} has not a wide CORS configuration" + + findings.append(report) + + return findings diff --git a/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy_test.py b/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy_test.py new file mode 100644 index 00000000..ce6d01c3 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_url_cors_policy/awslambda_function_url_cors_policy_test.py @@ -0,0 +1,163 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.awslambda.awslambda_service import ( + AuthType, + Function, + URLConfig, + URLConfigCORS, +) + +AWS_REGION = "us-east-1" + + +class Test_awslambda_function_url_cors_policy: + def test_no_functions(self): + lambda_client = mock.MagicMock + lambda_client.functions = {} + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_url_cors_policy.awslambda_function_url_cors_policy import ( + awslambda_function_url_cors_policy, + ) + + check = awslambda_function_url_cors_policy() + result = check.execute() + + assert len(result) == 0 + + def test_function_cors_asterisk(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + url_config=URLConfig( + auth_type=AuthType.NONE, + url="", + cors_config=URLConfigCORS(allow_origins=["*"]), + ), + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_url_cors_policy.awslambda_function_url_cors_policy import ( + awslambda_function_url_cors_policy, + ) + + check = awslambda_function_url_cors_policy() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Lambda function {function_name} URL has a wide CORS configuration" + ) + + def test_function_cors_not_wide(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "python3.9" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + url_config=URLConfig( + auth_type=AuthType.AWS_IAM, + url="", + cors_config=URLConfigCORS(allow_origins=["https://example.com"]), + ), + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_url_cors_policy.awslambda_function_url_cors_policy import ( + awslambda_function_url_cors_policy, + ) + + check = awslambda_function_url_cors_policy() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Lambda function {function_name} has not a wide CORS configuration" + ) + + def test_function_cors_wide_with_two_origins(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "python3.9" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + url_config=URLConfig( + auth_type=AuthType.AWS_IAM, + url="", + cors_config=URLConfigCORS( + allow_origins=["https://example.com", "*"] + ), + ), + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_url_cors_policy.awslambda_function_url_cors_policy import ( + awslambda_function_url_cors_policy, + ) + + check = awslambda_function_url_cors_policy() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Lambda function {function_name} URL has a wide CORS configuration" + ) diff --git a/providers/aws/services/awslambda/awslambda_function_url_public/__init__.py b/providers/aws/services/awslambda/awslambda_function_url_public/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public.metadata.json b/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public.metadata.json new file mode 100644 index 00000000..752b9cc0 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "awslambda_function_url_public", + "CheckTitle": "Check Public Lambda Function URL.", + "CheckType": [], + "ServiceName": "lambda", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name", + "Severity": "high", + "ResourceType": "AwsLambdaFunction", + "Description": "Check Public Lambda Function URL.", + "Risk": "Publicly accessible services could expose sensitive data to bad actors.", + "RelatedUrl": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html", + "Remediation": { + "Code": { + "CLI": "aws lambda update-function-url-config --region AWS_REGION --function-name FUNCTION-NAME --auth-type AWS_IAM", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Grant usage permission on a per-resource basis and applying least privilege principle.", + "Url": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public.py b/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public.py new file mode 100644 index 00000000..b7dcc1ee --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public.py @@ -0,0 +1,25 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.awslambda.awslambda_client import awslambda_client + +from providers.aws.services.awslambda.awslambda_service import AuthType + + +class awslambda_function_url_public(Check): + def execute(self): + findings = [] + for function in awslambda_client.functions.values(): + report = Check_Report(self.metadata) + report.region = function.region + report.resource_id = function.name + report.resource_arn = function.arn + if function.url_config: + if function.url_config.auth_type == AuthType.AWS_IAM: + report.status = "PASS" + report.status_extended = f"Lambda function {function.name} has not a publicly accessible function URL" + else: + report.status = "FAIL" + report.status_extended = f"Lambda function {function.name} has a publicly accessible function URL" + + findings.append(report) + + return findings diff --git a/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public_test.py b/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public_test.py new file mode 100644 index 00000000..13c63554 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_url_public/awslambda_function_url_public_test.py @@ -0,0 +1,118 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.awslambda.awslambda_service import ( + AuthType, + Function, + URLConfig, + URLConfigCORS, +) + +AWS_REGION = "us-east-1" + + +class Test_awslambda_function_url_public: + def test_no_functions(self): + lambda_client = mock.MagicMock + lambda_client.functions = {} + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_url_public.awslambda_function_url_public import ( + awslambda_function_url_public, + ) + + check = awslambda_function_url_public() + result = check.execute() + + assert len(result) == 0 + + def test_function_public_url(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + url_config=URLConfig( + auth_type=AuthType.NONE, + url="", + cors_config=URLConfigCORS(allow_origins=[]), + ), + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_url_public.awslambda_function_url_public import ( + awslambda_function_url_public, + ) + + check = awslambda_function_url_public() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Lambda function {function_name} has a publicly accessible function URL" + ) + + def test_function_private_url(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "python3.9" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + url_config=URLConfig( + auth_type=AuthType.AWS_IAM, + url="", + cors_config=URLConfigCORS(allow_origins=[]), + ), + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_url_public.awslambda_function_url_public import ( + awslambda_function_url_public, + ) + + check = awslambda_function_url_public() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Lambda function {function_name} has not a publicly accessible function URL" + ) diff --git a/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/__init__.py b/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes.metadata.json b/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes.metadata.json new file mode 100644 index 00000000..f0409d1d --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "awslambda_function_using_supported_runtimes", + "CheckTitle": "Find obsolete Lambda runtimes.", + "CheckType": [], + "ServiceName": "lambda", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:lambda:region:account-id:function/function-name", + "Severity": "medium", + "ResourceType": "AwsLambdaFunction", + "Description": "Find obsolete Lambda runtimes.", + "Risk": "If you have functions running on a runtime that will be deprecated in the next 60 days; Lambda notifies you by email that you should prepare by migrating your function to a supported runtime. In some cases; such as security issues that require a backwards-incompatible update; or software that does not support a long-term support (LTS) schedule; advance notice might not be possible. After a runtime is deprecated; Lambda might retire it completely at any time by disabling invocation. Deprecated runtimes are not eligible for security updates or technical support.", + "RelatedUrl": "https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html", + "Remediation": { + "Code": { + "CLI": "aws lambda update-function-configuration --region AWS-REGION --function-name FUNCTION-NAME --runtime 'RUNTIME'", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Test new runtimes as they are made available. Implement them in production as soon as possible.", + "Url": "https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes.py b/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes.py new file mode 100644 index 00000000..b80fa0a3 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes.py @@ -0,0 +1,24 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.awslambda.awslambda_client import awslambda_client +from config.config import get_config_var + + +class awslambda_function_using_supported_runtimes(Check): + def execute(self): + findings = [] + for function in awslambda_client.functions.values(): + report = Check_Report(self.metadata) + report.region = function.region + report.resource_id = function.name + report.resource_arn = function.arn + + if function.runtime in get_config_var("obsolete_lambda_runtimes"): + report.status = "FAIL" + report.status_extended = f"Lambda function {function.name} is using {function.runtime} which is obsolete" + else: + report.status = "PASS" + report.status_extended = f"Lambda function {function.name} is using {function.runtime} which is supported" + + findings.append(report) + + return findings diff --git a/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes_test.py b/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes_test.py new file mode 100644 index 00000000..c9293a3f --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_function_using_supported_runtimes/awslambda_function_using_supported_runtimes_test.py @@ -0,0 +1,126 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.services.awslambda.awslambda_service import Function + +AWS_REGION = "us-east-1" + + +def mock_get_config_var(config_var: str): + return [ + "python3.6", + "python2.7", + "nodejs4.3", + "nodejs4.3-edge", + "nodejs6.10", + "nodejs", + "nodejs8.10", + "nodejs10.x", + "dotnetcore1.0", + "dotnetcore2.0", + "dotnetcore2.1", + "ruby2.5", + ] + + +class Test_awslambda_function_using_supported_runtimes: + def test_no_functions(self): + lambda_client = mock.MagicMock + lambda_client.functions = {} + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_using_supported_runtimes.awslambda_function_using_supported_runtimes import ( + awslambda_function_using_supported_runtimes, + ) + + check = awslambda_function_using_supported_runtimes() + result = check.execute() + + assert len(result) == 0 + + def test_function_obsolete_runtime(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "nodejs4.3" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ), mock.patch( + "providers.aws.services.awslambda.awslambda_function_using_supported_runtimes.awslambda_function_using_supported_runtimes.get_config_var", + new=mock_get_config_var, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_using_supported_runtimes.awslambda_function_using_supported_runtimes import ( + awslambda_function_using_supported_runtimes, + ) + + check = awslambda_function_using_supported_runtimes() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Lambda function {function_name} is using {function_runtime} which is obsolete" + ) + + def test_function_supported_runtime(self): + lambda_client = mock.MagicMock + function_name = "test-lambda" + function_runtime = "python3.9" + function_arn = ( + f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function/{function_name}" + ) + lambda_client.functions = { + "function_name": Function( + name=function_name, + arn=function_arn, + region=AWS_REGION, + runtime=function_runtime, + ) + } + + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.Lambda", + new=lambda_client, + ), mock.patch( + "providers.aws.services.awslambda.awslambda_function_using_supported_runtimes.awslambda_function_using_supported_runtimes.get_config_var", + new=mock_get_config_var, + ): + # Test Check + from providers.aws.services.awslambda.awslambda_function_using_supported_runtimes.awslambda_function_using_supported_runtimes import ( + awslambda_function_using_supported_runtimes, + ) + + check = awslambda_function_using_supported_runtimes() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == function_name + assert result[0].resource_arn == function_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Lambda function {function_name} is using {function_runtime} which is supported" + ) diff --git a/providers/aws/services/awslambda/awslambda_service.py b/providers/aws/services/awslambda/awslambda_service.py new file mode 100644 index 00000000..33933aca --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_service.py @@ -0,0 +1,158 @@ +import threading + +from pydantic import BaseModel +from typing import Any +from lib.logger import logger +from providers.aws.aws_provider import generate_regional_clients +import requests +from enum import Enum +import io +import zipfile +import json + + +################## Lambda +class Lambda: + def __init__(self, audit_info): + self.service = "lambda" + self.session = audit_info.audit_session + self.audited_account = audit_info.audited_account + self.regional_clients = generate_regional_clients(self.service, audit_info) + self.functions = {} + self.__threading_call__(self.__list_functions__) + self.__threading_call__(self.__get_function__) + self.__threading_call__(self.__get_policy__) + self.__threading_call__(self.__get_function_url_config__) + + def __get_session__(self): + return self.session + + def __threading_call__(self, call): + threads = [] + for regional_client in self.regional_clients.values(): + threads.append(threading.Thread(target=call, args=(regional_client,))) + for t in threads: + t.start() + for t in threads: + t.join() + + def __list_functions__(self, regional_client): + logger.info("Lambda - Listing Functions...") + try: + list_functions_paginator = regional_client.get_paginator("list_functions") + for page in list_functions_paginator.paginate(): + for function in page["Functions"]: + lambda_name = function["FunctionName"] + lambda_arn = function["FunctionArn"] + lambda_runtime = function["Runtime"] + lambda_environment = function["Environment"]["Variables"] + self.functions[lambda_name] = Function( + name=lambda_name, + arn=lambda_arn, + runtime=lambda_runtime, + region=regional_client.region, + ) + if "Environment" in function: + self.functions[lambda_name].environment = lambda_environment + + except Exception as error: + logger.error( + f"{regional_client.region} --" + f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:" + f" {error}" + ) + + def __get_function__(self, regional_client): + logger.info("Lambda - Getting Function...") + try: + for function in self.functions.values(): + if function.region == regional_client.region: + function_information = regional_client.get_function( + FunctionName=function.name + ) + code_location_uri = function_information["Code"]["Location"] + raw_code_zip = requests.get(code_location_uri).content + self.functions[function.name].code = LambdaCode( + location=code_location_uri, + code_zip=zipfile.ZipFile(io.BytesIO(raw_code_zip)), + ) + + except Exception as error: + logger.error( + f"{regional_client.region} --" + f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:" + f" {error}" + ) + + def __get_policy__(self, regional_client): + logger.info("Lambda - Getting Policy...") + try: + for function in self.functions.values(): + if function.region == regional_client.region: + function_policy = regional_client.get_policy( + FunctionName=function.name + ) + self.functions[function.name].policy = json.loads( + function_policy["Policy"] + ) + + except Exception as error: + logger.error( + f"{regional_client.region} --" + f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:" + f" {error}" + ) + + def __get_function_url_config__(self, regional_client): + logger.info("Lambda - Getting Function URL Config...") + try: + for function in self.functions.values(): + if function.region == regional_client.region: + function_url_config = regional_client.get_function_url_config( + FunctionName=function.name + ) + self.functions[function.name].url_config = URLConfig( + auth_type=function_url_config["AuthType"], + url=function_url_config["FunctionUrl"], + cors_config=URLConfigCORS( + allow_origins=function_url_config["Cors"]["AllowOrigins"] + ), + ) + + except Exception as error: + logger.error( + f"{regional_client.region} --" + f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:" + f" {error}" + ) + + +class LambdaCode(BaseModel): + location: str + code_zip: Any + + +class AuthType(Enum): + NONE = "NONE" + AWS_IAM = "AWS_IAM" + + +class URLConfigCORS(BaseModel): + allow_origins: list[str] + + +class URLConfig(BaseModel): + auth_type: AuthType + url: str + cors_config: URLConfigCORS + + +class Function(BaseModel): + name: str + arn: str + runtime: str + environment: dict = None + region: str + policy: dict = None + code: LambdaCode = None + url_config: URLConfig = None diff --git a/providers/aws/services/awslambda/awslambda_service_test.py b/providers/aws/services/awslambda/awslambda_service_test.py new file mode 100644 index 00000000..c4835cf8 --- /dev/null +++ b/providers/aws/services/awslambda/awslambda_service_test.py @@ -0,0 +1,226 @@ +from unittest.mock import patch +from moto.core import DEFAULT_ACCOUNT_ID +from boto3 import client, resource, session +from providers.aws.lib.audit_info.audit_info import current_audit_info +from providers.aws.services.awslambda.awslambda_service import Lambda, AuthType +from providers.aws.lib.audit_info.models import AWS_Audit_Info +from moto import mock_lambda, mock_iam, mock_s3 +import zipfile +import io +import mock +from re import search +import tempfile +import os + +# Mock Test Region +AWS_REGION = "eu-west-1" + + +def create_zip_file(code: str = "") -> io.BytesIO: + zip_output = io.BytesIO() + zip_file = zipfile.ZipFile(zip_output, "w", zipfile.ZIP_DEFLATED) + if not code: + zip_file.writestr( + "lambda_function.py", + """ + def lambda_handler(event, context): + print("custom log event") + return event + """, + ) + else: + zip_file.writestr("lambda_function.py", code) + zip_file.close() + zip_output.seek(0) + return zip_output + + +def mock_request_get(_): + """Mock requests.get() to get the Lambda Code in Zip Format""" + mock_resp = mock.MagicMock + mock_resp.status_code = 200 + mock_resp.content = create_zip_file().read() + return mock_resp + + +# Mock generate_regional_clients() +def mock_generate_regional_clients(service, audit_info): + regional_client = audit_info.audit_session.client(service, region_name=AWS_REGION) + regional_client.region = AWS_REGION + return {AWS_REGION: regional_client} + + +@patch( + "providers.aws.services.awslambda.awslambda_service.generate_regional_clients", + new=mock_generate_regional_clients, +) +class Test_Lambda_Service: + def set_mocked_audit_info(self): + audit_info = AWS_Audit_Info( + original_session=None, + audit_session=session.Session( + profile_name=None, + botocore_session=None, + ), + audited_account=DEFAULT_ACCOUNT_ID, + audited_user_id=None, + audited_partition="aws", + audited_identity_arn=None, + profile=None, + profile_region=None, + credentials=None, + assumed_role_info=None, + audited_regions=None, + organizations_metadata=None, + ) + return audit_info + + # Test Lambda Client + def test__get_client__(self): + awslambda = Lambda(current_audit_info) + assert awslambda.regional_clients[AWS_REGION].__class__.__name__ == "Lambda" + + # Test Lambda Session + def test__get_session__(self): + awslambda = Lambda(current_audit_info) + assert awslambda.session.__class__.__name__ == "Session" + + # Test Lambda Service + def test__get_service__(self): + awslambda = Lambda(current_audit_info) + assert awslambda.service == "lambda" + + @mock_lambda + @mock_iam + @mock_s3 + def test__list_functions__(self): + # Create IAM Lambda Role + iam_client = client("iam", region_name=AWS_REGION) + iam_role = iam_client.create_role( + RoleName="test-lambda-role", + AssumeRolePolicyDocument="test-policy", + Path="/", + )["Role"]["Arn"] + # Create S3 Bucket + s3_client = resource("s3", region_name=AWS_REGION) + s3_client.create_bucket( + Bucket="test-bucket", + CreateBucketConfiguration={"LocationConstraint": AWS_REGION}, + ) + # Create Test Lambda + lambda_client = client("lambda", region_name=AWS_REGION) + lambda_name = "test-lambda" + resp = lambda_client.create_function( + FunctionName=lambda_name, + Runtime="python3.7", + Role=iam_role, + Handler="lambda_function.lambda_handler", + Code={"ZipFile": create_zip_file().read()}, + Description="test lambda function", + Timeout=3, + MemorySize=128, + PackageType="ZIP", + Publish=True, + VpcConfig={ + "SecurityGroupIds": ["sg-123abc"], + "SubnetIds": ["subnet-123abc"], + }, + Environment={"Variables": {"db-password": "test-password"}}, + ) + # Update Lambda Policy + lambda_policy = { + "Version": "2012-10-17", + "Id": "default", + "Statement": [ + { + "Action": "lambda:GetFunction", + "Principal": "*", + "Effect": "Allow", + "Resource": f"arn:aws:lambda:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:function:{lambda_name}", + "Sid": "test", + } + ], + } + _ = lambda_client.add_permission( + FunctionName=lambda_name, + StatementId="test", + Action="lambda:GetFunction", + Principal="*", + ) + # Create Function URL Config + _ = lambda_client.create_function_url_config( + FunctionName=lambda_name, + AuthType=AuthType.AWS_IAM.value, + Cors={ + "AllowCredentials": True, + "AllowHeaders": [ + "string", + ], + "AllowMethods": [ + "string", + ], + "AllowOrigins": [ + "*", + ], + "ExposeHeaders": [ + "string", + ], + "MaxAge": 123, + }, + ) + + lambda_arn = resp["FunctionArn"] + with mock.patch( + "providers.aws.services.awslambda.awslambda_service.requests.get", + new=mock_request_get, + ): + awslambda = Lambda(self.set_mocked_audit_info()) + + assert awslambda.functions + assert awslambda.functions[lambda_name].name == lambda_name + assert awslambda.functions[lambda_name].arn == lambda_arn + assert awslambda.functions[lambda_name].runtime == "python3.7" + assert awslambda.functions[lambda_name].environment == { + "db-password": "test-password" + } + assert awslambda.functions[lambda_name].region == AWS_REGION + assert awslambda.functions[lambda_name].policy == lambda_policy + + assert awslambda.functions[lambda_name].code + assert search( + f"s3://awslambda-{AWS_REGION}-tasks.s3-{AWS_REGION}.amazonaws.com", + awslambda.functions[lambda_name].code.location, + ) + + assert awslambda.functions[lambda_name].url_config + assert ( + awslambda.functions[lambda_name].url_config.auth_type + == AuthType.AWS_IAM + ) + assert search( + "lambda-url.eu-west-1.on.aws", + awslambda.functions[lambda_name].url_config.url, + ) + + assert awslambda.functions[lambda_name].url_config.cors_config + assert awslambda.functions[ + lambda_name + ].url_config.cors_config.allow_origins == ["*"] + + # Pending ZipFile tests + with tempfile.TemporaryDirectory() as tmp_dir_name: + awslambda.functions[lambda_name].code.code_zip.extractall(tmp_dir_name) + files_in_zip = next(os.walk(tmp_dir_name))[2] + assert len(files_in_zip) == 1 + assert files_in_zip[0] == "lambda_function.py" + with open(f"{tmp_dir_name}/{files_in_zip[0]}", "r") as lambda_code_file: + _ = lambda_code_file + pass + # assert ( + # lambda_code_file.read() + # == """ + # def lambda_handler(event, context): + # print("custom log event") + # return event + # """ + # ) diff --git a/providers/aws/services/lambda/check_extra7145 b/providers/aws/services/lambda/check_extra7145 deleted file mode 100644 index c62b7c61..00000000 --- a/providers/aws/services/lambda/check_extra7145 +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra7145="7.145" -CHECK_TITLE_extra7145="[extra7145] Check if Lambda functions have policies which allow access to any AWS account" -CHECK_SCORED_extra7145="NOT_SCORED" -CHECK_CIS_LEVEL_extra7145="EXTRA" -CHECK_SEVERITY_extra7145="Critical" -CHECK_ASFF_RESOURCE_TYPE_extra7145="AwsLambda" -CHECK_ALTERNATE_check7145="extra7145" -CHECK_SERVICENAME_extra7145="lambda" -CHECK_RISK_extra7145='Lambda function access to any AWS account may result security issues' -CHECK_REMEDIATION_extra7145='Ensure Lambda function policiy does not allow access to any account' -CHECK_DOC_extra7145='https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html' -CHECK_CAF_EPIC_extra7145='IAM' - -extra7145(){ - # "Check if lambda functions have policies which allow access to every aws account (Not Scored) (Not part of CIS benchmark)" - for regx in $REGIONS; do - LIST_OF_LAMBDA_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --query Functions[*].FunctionName --output text 2>&1) - if [[ $(echo "$LIST_OF_LAMBDA_FUNCTIONS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then - textInfo "$regx: Access Denied trying to list functions" "$regx" - continue - fi - if [[ $LIST_OF_LAMBDA_FUNCTIONS ]]; then - for lambdaFunction in $LIST_OF_LAMBDA_FUNCTIONS;do - FUNCTION_POLICY_STATEMENTS=$($AWSCLI lambda $PROFILE_OPT get-policy --region $regx --function-name $lambdaFunction --output json --query Policy 2>&1) - if [[ $FUNCTION_POLICY_STATEMENTS == *ResourceNotFoundException* ]]; then - textInfo "$regx: Lambda function $lambdaFunction doesn't have any policy" "$regx" "$lambdaFunction" - else - FUNCTION_POLICY_BAD_STATEMENTS=$(echo $FUNCTION_POLICY_STATEMENTS | jq '. | fromjson' | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")') - if [[ $FUNCTION_POLICY_BAD_STATEMENTS != "" ]]; then - textFail "$regx: Lambda function $lambdaFunction allows public access to any AWS account" "$regx" "$lambdaFunction" - else - textPass "$regx: Lambda function $lambdaFunction has policy which doesn't allow access to everyone having an AWS account" "$regx" "$lambdaFunction" - fi - fi - done - - else - textInfo "$regx: No lambda functions found" "$regx" - fi - done -} diff --git a/providers/aws/services/lambda/check_extra7179 b/providers/aws/services/lambda/check_extra7179 deleted file mode 100644 index 3b59f915..00000000 --- a/providers/aws/services/lambda/check_extra7179 +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra7179="7.179" -CHECK_TITLE_extra7179="[extra7179] Check Public Lambda Function URL" -CHECK_SCORED_extra7179="NOT_SCORED" -CHECK_CIS_LEVEL_extra7179="EXTRA" -CHECK_SEVERITY_extra7179="High" -CHECK_ASFF_RESOURCE_TYPE_extra7179="AwsLambdaFunction" -CHECK_ALTERNATE_check7179="extra7179" -CHECK_SERVICENAME_extra7179="lambda" -CHECK_RISK_extra7179='Publicly accessible services could expose sensitive data to bad actors.' -CHECK_REMEDIATION_extra7179='Grant usage permission on a per-resource basis and applying least privilege principle.' -CHECK_DOC_extra7179='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html' -CHECK_CAF_EPIC_extra7179='Infrastructure Security' - -extra7179(){ - # Check if Lambda function URL is public - # None --> Public - local PUBLIC_AUTH_TYPE="NONE" - # AWS_IAM --> Private - local PRIVATE_AUTH_TYPE="AWS_IAM" - - for regx in ${REGIONS}; do - LIST_OF_FUNCTIONS=$("${AWSCLI}" lambda list-functions ${PROFILE_OPT} \ - --region "${regx}" \ - --query 'Functions[*].FunctionName' \ - --output text 2>&1) - # Check errors - if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_FUNCTIONS}"; then - textInfo "${regx}: Access Denied trying to list Lambda functions" "${regx}" - continue - fi - if [[ -n "${LIST_OF_FUNCTIONS}" && $(tr '[:upper:]' '[:lower:]' <<< "${LIST_OF_FUNCTIONS}") != "none" ]]; then - for lambda_function in ${LIST_OF_FUNCTIONS}; do - AUTH_TYPE=$("${AWSCLI}" lambda list-function-url-configs ${PROFILE_OPT} \ - --function-name "${lambda_function}" \ - --region "${regx}" \ - --query 'FunctionUrlConfigs[0].AuthType' \ - --output text) - # Check errors - if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${AUTH_TYPE}"; then - textInfo "${regx}: Access Denied trying to get Lambda functions URLs configuration" "${regx}" - continue - fi - - if [[ "${AUTH_TYPE}" == "${PUBLIC_AUTH_TYPE}" ]]; then - textFail "${regx}: Lambda function ${lambda_function} has a publicly accessible function URL" "${regx}" "${lambda_function}" - elif [[ "${AUTH_TYPE}" == "${PRIVATE_AUTH_TYPE}" ]]; then - textPass "${regx}: Lambda function ${lambda_function} has not a publicly accessible function URL" "${regx}" "${lambda_function}" - else - textInfo "${regx}: Lambda function ${lambda_function} has not a function URL" "${regx}" "${lambda_function}" - fi - done - else - textInfo "${regx}: No Lambda functions found" "${regx}" - fi - done -} diff --git a/providers/aws/services/lambda/check_extra7180 b/providers/aws/services/lambda/check_extra7180 deleted file mode 100644 index bbbdd2c1..00000000 --- a/providers/aws/services/lambda/check_extra7180 +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra7180="7.180" -CHECK_TITLE_extra7180="[extra7180] Check Lambda Function URL CORS configuration" -CHECK_SCORED_extra7180="NOT_SCORED" -CHECK_CIS_LEVEL_extra7180="EXTRA" -CHECK_SEVERITY_extra7180="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra7180="AwsLambdaFunction" -CHECK_ALTERNATE_check7180="extra7180" -CHECK_SERVICENAME_extra7180="lambda" -CHECK_RISK_extra7180='Publicly accessible services could expose sensitive data to bad actors.' -CHECK_REMEDIATION_extra7180='Grant usage permission on a per-resource basis and applying least privilege principle.' -CHECK_DOC_extra7180='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html' -CHECK_CAF_EPIC_extra7180='Infrastructure Security' - -extra7180(){ - for regx in ${REGIONS}; do - LIST_OF_FUNCTIONS=$("${AWSCLI}" lambda list-functions ${PROFILE_OPT} \ - --region "${regx}" \ - --query 'Functions[*].FunctionName' \ - --output text 2>&1) - # Check errors - if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_FUNCTIONS}"; then - textInfo "${regx}: Access Denied trying to list Lambda functions" "${regx}" - continue - fi - if [[ -n "${LIST_OF_FUNCTIONS}" && $(tr '[:upper:]' '[:lower:]' <<< "${LIST_OF_FUNCTIONS}") != "none" ]]; then - for lambda_function in ${LIST_OF_FUNCTIONS}; do - # Check if Lambda function has an URL - LAMBDA_FUNCTION_URL=$("${AWSCLI}" lambda list-function-url-configs ${PROFILE_OPT} \ - --function-name "${lambda_function}" \ - --region "${regx}" \ - --query 'FunctionUrlConfigs[0].[FunctionUrl]' \ - --output text) - # Check errors - if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${AUTH_TYPE}"; then - textInfo "${regx}: Access Denied trying to get Lambda functions URLs" "${regx}" - continue - fi - - if [[ "${LAMBDA_FUNCTION_URL}" != "None" ]]; then - # Check CORS configuration - CORS_ALLOW_ORIGINS=$("${AWSCLI}" lambda get-function-url-config ${PROFILE_OPT} \ - --function-name "${lambda_function}" \ - --region "${regx}" \ - --query 'Cors.AllowOrigins' \ - --output text) - # Check errors - if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${AUTH_TYPE}"; then - textInfo "${regx}: Access Denied trying to get Lambda functions URLs configuration" "${regx}" - continue - fi - - # The * is on purpose to check allowed origins - if [[ "${CORS_ALLOW_ORIGINS}" =~ "*" ]]; then - textFail "$regx: Lambda function ${lambda_function} URL has a wide CORS configuration" "${regx}" "${lambda_function}" - elif [[ "${CORS_ALLOW_ORIGINS}" == "None" ]]; then - textFail "${regx}: Lambda function ${lambda_function} URL has not CORS configured" "${regx}" "${lambda_function}" - else - textPass "${regx}: Lambda function ${lambda_function} has not a wide CORS configuration" "${regx}" "${lambda_function}" - fi - - else - textInfo "${regx}: Lambda function ${lambda_function} has not a function URL" "${regx}" "${lambda_function}" - fi - done - else - textInfo "${regx}: No Lambda functions found" "${regx}" - fi - done -} diff --git a/providers/aws/services/lambda/check_extra720 b/providers/aws/services/lambda/check_extra720 deleted file mode 100644 index c8af3fca..00000000 --- a/providers/aws/services/lambda/check_extra720 +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra720="7.20" -CHECK_TITLE_extra720="[extra720] Check if Lambda functions invoke API operations are being recorded by CloudTrail" -CHECK_SCORED_extra720="NOT_SCORED" -CHECK_CIS_LEVEL_extra720="EXTRA" -CHECK_SEVERITY_extra720="Low" -CHECK_ASFF_RESOURCE_TYPE_extra720="AwsLambdaFunction" -CHECK_ALTERNATE_check720="extra720" -CHECK_SERVICENAME_extra720="lambda" -CHECK_RISK_extra720='If logs are not enabled; monitoring of service use and threat analysis is not possible.' -CHECK_REMEDIATION_extra720='Make sure you are logging information about Lambda operations. Create a lifecycle and use cases for each trail.' -CHECK_DOC_extra720='https://docs.aws.amazon.com/lambda/latest/dg/logging-using-cloudtrail.html' -CHECK_CAF_EPIC_extra720='Logging and Monitoring' - -extra720(){ - # "Check if Lambda functions invoke API operations are being recorded by CloudTrail " - for regx in $REGIONS; do - LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --query 'Functions[*].FunctionName' --output text 2>&1) - if [[ $(echo "$LIST_OF_FUNCTIONS" | grep AccessDenied) ]]; then - textInfo "$regx: Access Denied trying to list functions" "$regx" - continue - fi - if [[ $LIST_OF_FUNCTIONS ]]; then - LIST_OF_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query 'trailList[*].TrailARN' --output text 2>&1) - if [[ $(echo "$LIST_OF_TRAILS" | grep AccessDenied) ]]; then - textInfo "$regx: Access Denied trying to describe trails" "$regx" - continue - fi - for lambdafunction in $LIST_OF_FUNCTIONS; do - if [[ $LIST_OF_TRAILS ]]; then - for trail in $LIST_OF_TRAILS; do - FUNCTION_ENABLED_IN_TRAIL=$($AWSCLI cloudtrail get-event-selectors $PROFILE_OPT --trail-name $trail --region $regx --query "EventSelectors[*].DataResources[?Type == \`AWS::Lambda::Function\`].Values" --output text |xargs -n1| grep -E "^arn:${AWS_PARTITION}:lambda.*function:$lambdafunction$|^arn:${AWS_PARTITION}:lambda$") - if [[ $FUNCTION_ENABLED_IN_TRAIL ]]; then - textPass "$regx: Lambda function $lambdafunction enabled in trail $trail" "$regx" "$trail" - else - textFail "$regx: Lambda function $lambdafunction NOT enabled in trail $trail" "$regx" "$trail" - fi - done - # LIST_OF_MULTIREGION_TRAILS=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query "trailList[?IsMultiRegionTrail == \`true\`].Name" --output text) - # if [[ $LIST_OF_MULTIREGION_TRAILS ]]; then - # for trail in $LIST_OF_MULTIREGION_TRAILS; do - # REGION_OF_TRAIL=$($AWSCLI cloudtrail describe-trails $PROFILE_OPT --region $regx --query "trailList[?IsMultiRegionTrail == \`true\` && Name == \`$trail\` ].HomeRegion" --output text) - # FUNCTION_ENABLED_IN_THIS_REGION=$($AWSCLI cloudtrail get-event-selectors $PROFILE_OPT --trail-name $trail --region $REGION_OF_TRAIL --query "EventSelectors[*].DataResources[?Type == \`AWS::Lambda::Function\`].Values" --output text |xargs -n1| grep -E "^arn:aws:lambda.*function:$lambdafunction$") - # if [[ $FUNCTION_ENABLED_IN_THIS_REGION ]]; then - # textPass "$regx: Lambda function $lambdafunction enabled in trail $trail" "$regx" - # else - # textFail "$regx: Lambda function $lambdafunction NOT enabled in trail $trail" "$regx" - # fi - # done - # else - # textFail "$regx: Lambda function $lambdafunction is not being recorded!" "$regx" - # fi - else - textFail "$regx: Lambda function $lambdafunction is not being recorded no CloudTrail found!" "$regx" "$trail" - fi - done - else - textInfo "$regx: No Lambda functions found" "$regx" - fi - done -} diff --git a/providers/aws/services/lambda/check_extra759 b/providers/aws/services/lambda/check_extra759 deleted file mode 100644 index 9aaef3c9..00000000 --- a/providers/aws/services/lambda/check_extra759 +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra759="7.59" -CHECK_TITLE_extra759="[extra759] Find secrets in Lambda functions variables " -CHECK_SCORED_extra759="NOT_SCORED" -CHECK_CIS_LEVEL_extra759="EXTRA" -CHECK_SEVERITY_extra759="Critical" -CHECK_ASFF_RESOURCE_TYPE_extra759="AwsLambdaFunction" -CHECK_ALTERNATE_check759="extra759" -CHECK_SERVICENAME_extra759="lambda" -CHECK_RISK_extra759='The use of a hard-coded password increases the possibility of password guessing. If hard-coded passwords are used; it is possible that malicious users gain access through the account in question.' -CHECK_REMEDIATION_extra759='Use Secrets Manager to securely provide database credentials to Lambda functions and secure the databases as well as use the credentials to connect and query them without hardcoding the secrets in code or passing them through environmental variables. ' -CHECK_DOC_extra759='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html' -CHECK_CAF_EPIC_extra759='IAM' - -extra759(){ - SECRETS_TEMP_FOLDER="$PROWLER_DIR/secrets-$ACCOUNT_NUM-$PROWLER_START_TIME" - if [[ ! -d $SECRETS_TEMP_FOLDER ]]; then - # this folder is deleted once this check is finished - mkdir $SECRETS_TEMP_FOLDER - fi - - for regx in $REGIONS; do - CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector) - if [[ $? -eq 241 ]]; then - textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx" - else - LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --query Functions[*].FunctionName --output text 2>&1) - if [[ $(echo "$LIST_OF_FUNCTIONS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then - textInfo "$regx: Access Denied trying to list functions" "$regx" - continue - fi - if [[ $LIST_OF_FUNCTIONS ]]; then - for lambdafunction in $LIST_OF_FUNCTIONS;do - LAMBDA_FUNCTION_VARIABLES_FILE="$SECRETS_TEMP_FOLDER/extra759-$lambdafunction-$regx-variables.txt" - LAMBDA_FUNCTION_VARIABLES=$($AWSCLI lambda $PROFILE_OPT --region $regx get-function-configuration --function-name $lambdafunction --query 'Environment.Variables' --output json > $LAMBDA_FUNCTION_VARIABLES_FILE) - if [ -s $LAMBDA_FUNCTION_VARIABLES_FILE ];then - # Implementation using https://github.com/Yelp/detect-secrets - FINDINGS=$(secretsDetector file $LAMBDA_FUNCTION_VARIABLES_FILE) - if [[ $FINDINGS -eq 0 ]]; then - textPass "$regx: No secrets found in Lambda function $lambdafunction variables" "$regx" "$lambdafunction" - # delete file if nothing interesting is there - rm -f $LAMBDA_FUNCTION_VARIABLES_FILE - else - textFail "$regx: Potential secret found in Lambda function $lambdafunction variables" "$regx" "$lambdafunction" - # delete file to not leave trace, user must look at the function - rm -f $LAMBDA_FUNCTION_VARIABLES_FILE - fi - else - textInfo "$regx: Lambda function $stalambdafunction has not variables" "$regx" - fi - done - else - textInfo "$regx: No Lambda functions found" "$regx" - fi - fi - done - rm -rf $SECRETS_TEMP_FOLDER -} diff --git a/providers/aws/services/lambda/check_extra760 b/providers/aws/services/lambda/check_extra760 deleted file mode 100644 index 7383d11b..00000000 --- a/providers/aws/services/lambda/check_extra760 +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra760="7.60" -CHECK_TITLE_extra760="[extra760] Find secrets in Lambda functions code " -CHECK_SCORED_extra760="NOT_SCORED" -CHECK_CIS_LEVEL_extra760="EXTRA" -CHECK_SEVERITY_extra760="Critical" -CHECK_ASFF_RESOURCE_TYPE_extra760="AwsLambdaFunction" -CHECK_ALTERNATE_check760="extra760" -CHECK_SERVICENAME_extra760="lambda" -CHECK_RISK_extra760='The use of a hard-coded password increases the possibility of password guessing. If hard-coded passwords are used; it is possible that malicious users gain access through the account in question.' -CHECK_REMEDIATION_extra760='Use Secrets Manager to securely provide database credentials to Lambda functions and secure the databases as well as use the credentials to connect and query them without hardcoding the secrets in code or passing them through environmental variables. ' -CHECK_DOC_extra760='https://docs.aws.amazon.com/secretsmanager/latest/userguide/lambda-functions.html' -CHECK_CAF_EPIC_extra760='IAM' - -extra760(){ - SECRETS_TEMP_FOLDER="${PROWLER_DIR}/secrets-${ACCOUNT_NUM}-${PROWLER_START_TIME}" - if [[ ! -d "${SECRETS_TEMP_FOLDER}" ]]; then - # this folder is deleted once this check is finished - mkdir "${SECRETS_TEMP_FOLDER}" - fi - - for regx in ${REGIONS}; do - CHECK_DETECT_SECRETS_INSTALLATION=$(secretsDetector) - if [[ $? -eq 241 ]]; then - textInfo "$regx: python library detect-secrets not found. Make sure it is installed correctly." "$regx" - else - LIST_OF_FUNCTIONS=$("${AWSCLI}" lambda list-functions ${PROFILE_OPT} --region "${regx}" --query 'Functions[*].FunctionName' --output text 2>&1) - if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LIST_OF_FUNCTIONS}"; then - textInfo "${regx}: Access Denied trying to list Lambda functions" "${regx}" - continue - fi - if [[ -n "${LIST_OF_FUNCTIONS}" && $(tr '[:upper:]' '[:lower:]' <<< "${LIST_OF_FUNCTIONS}") != "none" ]]; then - for lambdafunction in ${LIST_OF_FUNCTIONS}; do - LAMBDA_FUNCTION_FOLDER="${SECRETS_TEMP_FOLDER}/extra760-${lambdafunction}-${regx}" - LAMBDA_FUNCTION_FILE="${lambdafunction}-code.zip" - LAMBDA_CODE_LOCATION=$("${AWSCLI}" lambda get-function ${PROFILE_OPT} --region "${regx}" --function-name "${lambdafunction}" --query 'Code.Location' --output text 2>&1) - if grep -q -E 'AccessDenied|UnauthorizedOperation|AuthorizationError' <<< "${LAMBDA_CODE_LOCATION}"; then - textInfo "${regx}: Access Denied trying to get Lambda functions" "${regx}" "${lambdafunction}" - continue - fi - - mkdir "${LAMBDA_FUNCTION_FOLDER}" - - # DOWNLOAD the code in a zip file - CURL_ERROR=$(curl -s --show-error "${LAMBDA_CODE_LOCATION}" -o "${LAMBDA_FUNCTION_FOLDER}/${LAMBDA_FUNCTION_FILE}" 2>&1) - if [[ -n "${CURL_ERROR}" ]]; then - textInfo "${regx}: Error trying to get Lambda function code for ${lambdafunction} - ${CURL_ERROR}" "${regx}" "${lambdafunction}" - # delete files to not leave trace, user must look at the function - if [[ -d "${LAMBDA_FUNCTION_FOLDER}" ]]; then - rm -fr "${LAMBDA_FUNCTION_FOLDER}" - fi - continue - fi - if ! grep -q 'Zip archive data' <(file "${LAMBDA_FUNCTION_FOLDER}/${LAMBDA_FUNCTION_FILE}"); then - textInfo "${regx}: Error trying to get Lambda function code for ${lambdafunction}. File is not a Zip" "${regx}" "${lambdafunction}" - # delete files to not leave trace, user must look at the function - if [[ -d "${LAMBDA_FUNCTION_FOLDER}" ]]; then - rm -fr "${LAMBDA_FUNCTION_FOLDER}" - fi - continue - fi - - unzip -qq "${LAMBDA_FUNCTION_FOLDER}/${LAMBDA_FUNCTION_FILE}" -d "${LAMBDA_FUNCTION_FOLDER}" && { - FINDINGS=$(secretsDetector folder "${LAMBDA_FUNCTION_FOLDER}") - if [[ ${FINDINGS} -eq 0 ]]; then - textPass "${regx}: No secrets found in Lambda function ${lambdafunction} code" "${regx}" "${lambdafunction}" - else - textFail "${regx}: Potential secret found in Lambda function ${lambdafunction} code" "${regx}" "${lambdafunction}" - fi - } - - # delete files to not leave trace, user must look at the function - if [[ -d "${LAMBDA_FUNCTION_FOLDER}" ]]; then - rm -fr "${LAMBDA_FUNCTION_FOLDER}" - fi - done - else - textInfo "${regx}: No Lambda functions found" "${regx}" - fi - fi - done - - if [[ -d "${SECRETS_TEMP_FOLDER}" ]]; then - rm -fr "${SECRETS_TEMP_FOLDER}" - fi -} diff --git a/providers/aws/services/lambda/check_extra762 b/providers/aws/services/lambda/check_extra762 deleted file mode 100644 index 9fc6dc6f..00000000 --- a/providers/aws/services/lambda/check_extra762 +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. -CHECK_ID_extra762="7.62" -CHECK_TITLE_extra762="[extra762] Find obsolete Lambda runtimes " -CHECK_SCORED_extra762="NOT_SCORED" -CHECK_CIS_LEVEL_extra762="EXTRA" -CHECK_SEVERITY_extra762="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra762="AwsLambdaFunction" -CHECK_ALTERNATE_check762="extra762" -CHECK_SERVICENAME_extra762="lambda" -CHECK_RISK_extra762='If you have functions running on a runtime that will be deprecated in the next 60 days; Lambda notifies you by email that you should prepare by migrating your function to a supported runtime. In some cases; such as security issues that require a backwards-incompatible update; or software that does not support a long-term support (LTS) schedule; advance notice might not be possible. After a runtime is deprecated; Lambda might retire it completely at any time by disabling invocation. Deprecated runtimes are not eligible for security updates or technical support.' -CHECK_REMEDIATION_extra762='Test new runtimes as they are made available. Implement them in production as soon as possible.' -CHECK_DOC_extra762='https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html' -CHECK_CAF_EPIC_extra762='Infrastructure Security' - -extra762(){ - - # regex to match OBSOLETE runtimes in string functionName%runtime - # https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html - OBSOLETE='%(nodejs4.3|nodejs4.3-edge|nodejs6.10|nodejs8.10|dotnetcore1.0|dotnetcore2.0)' - - for regx in $REGIONS; do - LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --output text --query 'Functions[*].{R:Runtime,N:FunctionName}' 2>&1| tr "\t" "%" ) - if [[ $(echo "$LIST_OF_FUNCTIONS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then - textInfo "$regx: Access Denied trying to list functions" "$regx" - continue - fi - if [[ $LIST_OF_FUNCTIONS ]]; then - for lambdafunction in $LIST_OF_FUNCTIONS;do - fname=$(echo "$lambdafunction" | cut -d'%' -f1) - runtime=$(echo "$lambdafunction" | cut -d'%' -f2) - if echo "$lambdafunction" | grep -Eq $OBSOLETE ; then - textFail "$regx: Obsolete runtime: ${runtime} used by: ${fname}" "$regx" "${fname}" - else - textPass "$regx: Supported runtime: ${runtime} used by: ${fname}" "$regx" "${fname}" - fi - done - else - textInfo "$regx: No Lambda functions found" "$regx" - fi - done -} diff --git a/providers/aws/services/lambda/check_extra798 b/providers/aws/services/lambda/check_extra798 deleted file mode 100644 index 21604e16..00000000 --- a/providers/aws/services/lambda/check_extra798 +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash - -# Prowler - the handy cloud security tool (copyright 2018) by Toni de la Fuente -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy -# of the License at http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed -# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# CONDITIONS OF ANY KIND, either express or implied. See the License for the -# specific language governing permissions and limitations under the License. - -CHECK_ID_extra798="7.98" -CHECK_TITLE_extra798="[extra798] Check if Lambda functions have resource-based policy set as Public" -CHECK_SCORED_extra798="NOT_SCORED" -CHECK_CIS_LEVEL_extra798="EXTRA" -CHECK_SEVERITY_extra798="Critical" -CHECK_ASFF_RESOURCE_TYPE_extra798="AwsLambdaFunction" -CHECK_ALTERNATE_check798="extra798" -CHECK_SERVICENAME_extra798="lambda" -CHECK_RISK_extra798='Publicly accessible services could expose sensitive data to bad actors.' -CHECK_REMEDIATION_extra798='Grant usage permission on a per-resource basis and applying least privilege principle.' -CHECK_DOC_extra798='https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html' -CHECK_CAF_EPIC_extra798='Infrastructure Security' - -extra798(){ - for regx in $REGIONS; do - LIST_OF_FUNCTIONS=$($AWSCLI lambda list-functions $PROFILE_OPT --region $regx --output text --query 'Functions[*].FunctionName' 2>&1) - if [[ $(echo "$LIST_OF_FUNCTIONS" | grep -E 'AccessDenied|UnauthorizedOperation|AuthorizationError') ]]; then - textInfo "$regx: Access Denied trying to list functions" "$regx" - continue - fi - if [[ $LIST_OF_FUNCTIONS ]]; then - for lambdafunction in $LIST_OF_FUNCTIONS; do - # get the policy per function - FUNCTION_POLICY=$($AWSCLI lambda get-policy $PROFILE_OPT --region $regx --function-name $lambdafunction --query Policy --output text 2>/dev/null) - if [[ $FUNCTION_POLICY ]]; then - FUNCTION_POLICY_ALLOW_ALL=$(echo $FUNCTION_POLICY \ - | jq '.Statement[] | select(.Effect=="Allow") | select(.Principal=="*" or .Principal.AWS=="*" or .Principal.CanonicalUser=="*")') - if [[ $FUNCTION_POLICY_ALLOW_ALL ]]; then - textFail "$regx: Lambda function $lambdafunction has a policy with public access" "$regx" "$lambdafunction" - else - textPass "$regx: Lambda function $lambdafunction has a policy resource-based policy and is not public" "$regx" "$lambdafunction" - fi - else - textPass "$regx: Lambda function $lambdafunction does not have resource-based policy" "$regx" "$lambdafunction" - fi - done - else - textInfo "$regx: No Lambda functions found" "$regx" - fi - done -}