diff --git a/Pipfile.lock b/Pipfile.lock index 9da75af9..92d6a8a9 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,1228 +1,1228 @@ { - "_meta": { - "hash": { - "sha256": "881edb3306efd59b84c75bd2bff5acbc29397eb7f12321203c62a013f0491e2e" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.9" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] + "_meta": { + "hash": { + "sha256": "881edb3306efd59b84c75bd2bff5acbc29397eb7f12321203c62a013f0491e2e" }, - "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" - } + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" }, - "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" - } + "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:180b4413db1211836c622adfc16dd40a7b99b86cac894bc6e17ddea9d282ab14", + "sha256:73da24667fe1351cef0f402ee9cd4589a0a9d97b617caca3c25b5cdc38f9d62c" + ], + "index": "pypi", + "version": "==1.26.12" + }, + "botocore": { + "hashes": [ + "sha256:3149b102e3c26c935acef6c330d0f46c717820d118886e983b6e81c306f31405", + "sha256:fdae73306a41a30697be300bdecb1e0d560d453c6d748891856beb87e9f6573f" + ], + "index": "pypi", + "version": "==1.29.12" + }, + "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" } + }, + "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:180b4413db1211836c622adfc16dd40a7b99b86cac894bc6e17ddea9d282ab14", + "sha256:73da24667fe1351cef0f402ee9cd4589a0a9d97b617caca3c25b5cdc38f9d62c" + ], + "index": "pypi", + "version": "==1.26.12" + }, + "botocore": { + "hashes": [ + "sha256:3149b102e3c26c935acef6c330d0f46c717820d118886e983b6e81c306f31405", + "sha256:fdae73306a41a30697be300bdecb1e0d560d453c6d748891856beb87e9f6573f" + ], + "index": "pypi", + "version": "==1.29.12" + }, + "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_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: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_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_version < '4' and python_full_version >= '3.6.1'", + "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_version < '4'", + "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_version < '4'", + "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_version < '4'", + "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_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_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/lib/check/check.py b/lib/check/check.py index 86576679..086f3dd9 100644 --- a/lib/check/check.py +++ b/lib/check/check.py @@ -100,9 +100,6 @@ def print_services(service_list: set): def print_checks(provider: str, check_list: set, bulk_checks_metadata: dict): - print( - f"There are {Fore.YELLOW}{len(check_list)}{Style.RESET_ALL} available checks: \n" - ) for check in check_list: try: print( @@ -112,6 +109,9 @@ def print_checks(provider: str, check_list: set, bulk_checks_metadata: dict): logger.error( f"Check {error} was not found for the {provider.upper()} provider" ) + print( + f"\nThere are {Fore.YELLOW}{len(check_list)}{Style.RESET_ALL} available checks.\n" + ) # List available groups 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 index 85091e52..75b0dc81 100644 --- 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 @@ -1,211 +1,211 @@ -# from datetime import datetime -# from unittest import mock +from unittest import mock -# from boto3 import session -# from moto.core import DEFAULT_ACCOUNT_ID +from boto3 import client, session +from mock import patch +from moto import mock_cloudtrail, mock_s3 +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 +from providers.aws.lib.audit_info.audit_info import AWS_Audit_Info +from providers.aws.services.awslambda.awslambda_service import Function -# AWS_REGION = "us-east-1" +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 +# 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} -# 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, -# ) +# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client +@patch( + "providers.aws.services.accessanalyzer.accessanalyzer_service.generate_regional_clients", + new=mock_generate_regional_clients, +) +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 -# check = ( -# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled() -# ) -# result = check.execute() + @mock_cloudtrail + def test_no_functions(self): + lambda_client = mock.MagicMock + lambda_client.functions = {} -# assert len(result) == 0 + from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail -# 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.lib.audit_info.audit_info.current_audit_info", + self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.cloudtrail_client", + new=Cloudtrail(self.set_mocked_audit_info()), + ): + # 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, + ) -# 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, -# ): + check = ( + awslambda_function_invoke_api_operations_cloudtrail_logging_enabled() + ) + result = check.execute() -# # 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, -# ) + assert len(result) == 0 -# check = ( -# awslambda_function_invoke_api_operations_cloudtrail_logging_enabled() -# ) -# result = check.execute() + @mock_cloudtrail + @mock_s3 + 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, + ) + } -# 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" -# ) + # CloudTrail Client + cloudtrail_client = client("cloudtrail", region_name=AWS_REGION) + s3_client = client("s3", region_name=AWS_REGION) + trail_name = "test-trail" + bucket_name = "test-bucket" + s3_client.create_bucket(Bucket=bucket_name) + cloudtrail_client.create_trail( + Name=trail_name, S3BucketName=bucket_name, IsMultiRegionTrail=False + ) -# 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": [], -# } -# ], -# ) -# ] + from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail -# 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, -# ): + 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.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.cloudtrail_client", + new=Cloudtrail(self.set_mocked_audit_info()), + ): -# # -# # 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, -# ) + # 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() + 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}" -# ) + 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" + ) + + @mock_cloudtrail + @mock_s3 + 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 = client("cloudtrail", region_name=AWS_REGION) + s3_client = client("s3", region_name=AWS_REGION) + trail_name = "test-trail" + bucket_name = "test-bucket" + s3_client.create_bucket(Bucket=bucket_name) + cloudtrail_client.create_trail( + Name=trail_name, S3BucketName=bucket_name, IsMultiRegionTrail=False + ) + _ = cloudtrail_client.put_event_selectors( + TrailName=trail_name, + EventSelectors=[ + { + "ReadWriteType": "All", + "IncludeManagementEvents": True, + "DataResources": [ + {"Type": "AWS::Lambda::Function", "Values": [function_arn]} + ], + } + ], + ) + + from providers.aws.services.cloudtrail.cloudtrail_service import Cloudtrail + + 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.awslambda.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.awslambda_function_invoke_api_operations_cloudtrail_logging_enabled.cloudtrail_client", + new=Cloudtrail(self.set_mocked_audit_info()), + ): + + # 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/ec2/ec2_service.py b/providers/aws/services/ec2/ec2_service.py index 6d5fbd60..f19fd6e4 100644 --- a/providers/aws/services/ec2/ec2_service.py +++ b/providers/aws/services/ec2/ec2_service.py @@ -10,6 +10,7 @@ class EC2: def __init__(self, audit_info): self.service = "ec2" self.session = audit_info.audit_session + self.audited_partition = audit_info.audited_partition self.audited_account = audit_info.audited_account self.regional_clients = generate_regional_clients(self.service, audit_info) self.instances = [] @@ -264,11 +265,14 @@ class EC2: association_id = address["AssociationId"] if "AllocationId" in address: allocation_id = address["AllocationId"] + elastic_ip_arn = f"arn:{self.audited_partition}:ec2:{regional_client.region}:{self.audited_account}:eip-allocation/{allocation_id}" + self.elastic_ips.append( ElasticIP( public_ip, association_id, allocation_id, + elastic_ip_arn, regional_client.region, ) ) @@ -403,13 +407,15 @@ class NetworkACL: class ElasticIP: public_ip: str association_id: str + arn: str allocation_id: str region: str - def __init__(self, public_ip, association_id, allocation_id, region): + def __init__(self, public_ip, association_id, allocation_id, arn, region): self.public_ip = public_ip self.association_id = association_id self.allocation_id = allocation_id + self.arn = arn self.region = region diff --git a/providers/aws/services/ec2/ec2_service_test.py b/providers/aws/services/ec2/ec2_service_test.py index 982002a9..9abc6f18 100644 --- a/providers/aws/services/ec2/ec2_service_test.py +++ b/providers/aws/services/ec2/ec2_service_test.py @@ -205,8 +205,14 @@ class Test_EC2_Service: def test__describe_addresses__(self): # Generate EC2 Client ec2_client = client("ec2", region_name=AWS_REGION) - ec2_client.allocate_address(Domain="vpc", Address="127.38.43.222") + allocation_id = ec2_client.allocate_address( + Domain="vpc", Address="127.38.43.222" + )["AllocationId"] # EC2 client for this test class audit_info = self.set_mocked_audit_info() ec2 = EC2(audit_info) assert "127.38.43.222" in str(ec2.elastic_ips) + assert ( + ec2.elastic_ips[0].arn + == f"arn:aws:ec2:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:eip-allocation/{allocation_id}" + ) diff --git a/providers/aws/services/elb/elb_service.py b/providers/aws/services/elb/elb_service.py index 8b1e15af..a2c8231a 100644 --- a/providers/aws/services/elb/elb_service.py +++ b/providers/aws/services/elb/elb_service.py @@ -12,6 +12,8 @@ class ELB: def __init__(self, audit_info): self.service = "elb" self.session = audit_info.audit_session + self.audited_partition = audit_info.audited_partition + self.audited_account = audit_info.audited_account self.regional_clients = generate_regional_clients(self.service, audit_info) self.loadbalancers = [] self.__threading_call__(self.__describe_load_balancers__) @@ -48,6 +50,7 @@ class ELB: self.loadbalancers.append( LoadBalancer( name=elb["LoadBalancerName"], + arn=f"arn:{self.audited_partition}:elasticloadbalancing:{regional_client.region}:{self.audited_account}:loadbalancer/{elb['LoadBalancerName']}", dns=elb["DNSName"], region=regional_client.region, scheme=elb["Scheme"], @@ -85,6 +88,7 @@ class Listener(BaseModel): class LoadBalancer(BaseModel): name: str dns: str + arn: str region: str scheme: str access_logs: Optional[bool] diff --git a/providers/aws/services/elb/elb_service_test.py b/providers/aws/services/elb/elb_service_test.py index 78c96693..84c3bab2 100644 --- a/providers/aws/services/elb/elb_service_test.py +++ b/providers/aws/services/elb/elb_service_test.py @@ -83,6 +83,10 @@ class Test_ELB_Service: assert elb.loadbalancers[0].name == "my-lb" assert elb.loadbalancers[0].region == AWS_REGION assert elb.loadbalancers[0].scheme == "internal" + assert ( + elb.loadbalancers[0].arn + == f"arn:aws:elasticloadbalancing:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:loadbalancer/my-lb" + ) # Test ELB Describe Load Balancers Attributes @mock_ec2 @@ -124,3 +128,7 @@ class Test_ELB_Service: assert elb.loadbalancers[0].region == AWS_REGION assert elb.loadbalancers[0].scheme == "internal" assert elb.loadbalancers[0].access_logs + assert ( + elb.loadbalancers[0].arn + == f"arn:aws:elasticloadbalancing:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:loadbalancer/my-lb" + ) diff --git a/providers/aws/services/globalaccelerator/__init__.py b/providers/aws/services/globalaccelerator/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/globalaccelerator/globalaccelerator_client.py b/providers/aws/services/globalaccelerator/globalaccelerator_client.py new file mode 100644 index 00000000..23a026ac --- /dev/null +++ b/providers/aws/services/globalaccelerator/globalaccelerator_client.py @@ -0,0 +1,6 @@ +from providers.aws.lib.audit_info.audit_info import current_audit_info +from providers.aws.services.globalaccelerator.globalaccelerator_service import ( + GlobalAccelerator, +) + +globalaccelerator_client = GlobalAccelerator(current_audit_info) diff --git a/providers/aws/services/globalaccelerator/globalaccelerator_service.py b/providers/aws/services/globalaccelerator/globalaccelerator_service.py new file mode 100644 index 00000000..4d3004b9 --- /dev/null +++ b/providers/aws/services/globalaccelerator/globalaccelerator_service.py @@ -0,0 +1,46 @@ +from pydantic import BaseModel +from lib.logger import logger +from providers.aws.aws_provider import get_region_global_service + + +################### GlobalAccelerator +class GlobalAccelerator: + def __init__(self, audit_info): + self.service = "globalaccelerator" + self.session = audit_info.audit_session + self.audited_account = audit_info.audited_account + self.region = get_region_global_service(audit_info) + self.client = self.session.client(self.service, self.region) + self.accelerators = {} + self.__list_accelerators__() + + def __get_session__(self): + return self.session + + def __list_accelerators__(self): + logger.info("GlobalAccelerator - Listing Accelerators...") + try: + list_accelerators_paginator = self.client.get_paginator("list_accelerators") + for page in list_accelerators_paginator.paginate(): + for accelerator in page["Accelerators"]: + accelerator_arn = accelerator["AcceleratorArn"] + accelerator_name = accelerator["Name"] + enabled = accelerator["Enabled"] + self.accelerators[accelerator_name] = Accelerator( + name=accelerator_name, + arn=accelerator_arn, + region=self.region, + enabled=enabled, + ) + + except Exception as error: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + +class Accelerator(BaseModel): + arn: str + name: str + region: str + enabled: bool diff --git a/providers/aws/services/globalaccelerator/globalaccelerator_service_test.py b/providers/aws/services/globalaccelerator/globalaccelerator_service_test.py new file mode 100644 index 00000000..ae8f01e7 --- /dev/null +++ b/providers/aws/services/globalaccelerator/globalaccelerator_service_test.py @@ -0,0 +1,105 @@ +from providers.aws.lib.audit_info.models import AWS_Audit_Info +from providers.aws.services.globalaccelerator.globalaccelerator_service import ( + GlobalAccelerator, +) +from mock import patch +from moto.core import DEFAULT_ACCOUNT_ID +import botocore +from boto3 import session + +# Mock Test Region +AWS_REGION = "eu-west-1" + +# Mocking Access Analyzer Calls +make_api_call = botocore.client.BaseClient._make_api_call + + +def mock_make_api_call(self, operation_name, kwarg): + """We have to mock every AWS API call using Boto3""" + if operation_name == "ListAccelerators": + return { + "Accelerators": [ + { + "AcceleratorArn": f"arn:aws:globalaccelerator::{DEFAULT_ACCOUNT_ID}:accelerator/5555abcd-abcd-5555-abcd-5555EXAMPLE1", + "Name": "TestAccelerator", + "IpAddressType": "IPV4", + "Enabled": True, + "IpSets": [ + { + "IpFamily": "IPv4", + "IpAddresses": ["192.0.2.250", "198.51.100.52"], + } + ], + "DnsName": "5a5a5a5a5a5a5a5a.awsglobalaccelerator.com", + "Status": "DEPLOYED", + "CreatedTime": 1552424416.0, + "LastModifiedTime": 1569375641.0, + } + ] + } + if operation_name == "GetSubscriptionState": + return {"SubscriptionState": "ACTIVE"} + + return make_api_call(self, operation_name, kwarg) + + +# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client +@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) +class Test_GlobalAccelerator_Service: + # 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=DEFAULT_ACCOUNT_ID, + audited_user_id=None, + audited_partition="aws", + audited_identity_arn=None, + profile=None, + profile_region=AWS_REGION, + credentials=None, + assumed_role_info=None, + audited_regions=None, + organizations_metadata=None, + ) + return audit_info + + # Test GlobalAccelerator Service + def test_service(self): + # GlobalAccelerator client for this test class + audit_info = self.set_mocked_audit_info() + globalaccelerator = GlobalAccelerator(audit_info) + assert globalaccelerator.service == "globalaccelerator" + + # Test GlobalAccelerator Client + def test_client(self): + # GlobalAccelerator client for this test class + audit_info = self.set_mocked_audit_info() + globalaccelerator = GlobalAccelerator(audit_info) + assert globalaccelerator.client.__class__.__name__ == "GlobalAccelerator" + + # Test GlobalAccelerator Session + def test__get_session__(self): + # GlobalAccelerator client for this test class + audit_info = self.set_mocked_audit_info() + globalaccelerator = GlobalAccelerator(audit_info) + assert globalaccelerator.session.__class__.__name__ == "Session" + + def test__list_accelerators__(self): + # GlobalAccelerator client for this test class + audit_info = self.set_mocked_audit_info() + globalaccelerator = GlobalAccelerator(audit_info) + + accelerator_arn = f"arn:aws:globalaccelerator::{DEFAULT_ACCOUNT_ID}:accelerator/5555abcd-abcd-5555-abcd-5555EXAMPLE1" + accelerator_name = "TestAccelerator" + + assert globalaccelerator.accelerators + assert len(globalaccelerator.accelerators) == 1 + assert globalaccelerator.accelerators[accelerator_name] + assert globalaccelerator.accelerators[accelerator_name].name == accelerator_name + assert globalaccelerator.accelerators[accelerator_name].arn == accelerator_arn + assert globalaccelerator.accelerators[accelerator_name].region == AWS_REGION + assert globalaccelerator.accelerators[accelerator_name].enabled diff --git a/providers/aws/services/kms/kms_key_not_publicly_accessible/kms_key_not_publicly_accessible.py b/providers/aws/services/kms/kms_key_not_publicly_accessible/kms_key_not_publicly_accessible.py index 2911469e..74845d83 100644 --- a/providers/aws/services/kms/kms_key_not_publicly_accessible/kms_key_not_publicly_accessible.py +++ b/providers/aws/services/kms/kms_key_not_publicly_accessible/kms_key_not_publicly_accessible.py @@ -22,7 +22,7 @@ class kms_key_not_publicly_accessible(Check): report.status_extended = ( f"KMS key {key.id} may be publicly accessible!" ) - else: + elif "AWS" in statement["Principal"]: if type(statement["Principal"]["AWS"]) == str: principals = [statement["Principal"]["AWS"]] else: diff --git a/providers/aws/services/route53/route53_public_hosted_zones_cloudwatch_logging_enabled/route53_public_hosted_zones_cloudwatch_logging_enabled_test.py b/providers/aws/services/route53/route53_public_hosted_zones_cloudwatch_logging_enabled/route53_public_hosted_zones_cloudwatch_logging_enabled_test.py index e5f2a9dc..587393bd 100644 --- a/providers/aws/services/route53/route53_public_hosted_zones_cloudwatch_logging_enabled/route53_public_hosted_zones_cloudwatch_logging_enabled_test.py +++ b/providers/aws/services/route53/route53_public_hosted_zones_cloudwatch_logging_enabled/route53_public_hosted_zones_cloudwatch_logging_enabled_test.py @@ -37,6 +37,7 @@ class Test_route53_public_hosted_zones_cloudwatch_logging_enabled: route53.hosted_zones = { hosted_zone_name: HostedZone( name=hosted_zone_name, + arn=f"arn:aws:route53:::{hosted_zone_id}", id=hosted_zone_id, private_zone=False, region=AWS_REGION, @@ -72,6 +73,7 @@ class Test_route53_public_hosted_zones_cloudwatch_logging_enabled: route53.hosted_zones = { hosted_zone_name: HostedZone( name=hosted_zone_name, + arn=f"arn:aws:route53:::{hosted_zone_id}", id=hosted_zone_id, private_zone=False, region=AWS_REGION, @@ -106,6 +108,7 @@ class Test_route53_public_hosted_zones_cloudwatch_logging_enabled: route53.hosted_zones = { hosted_zone_name: HostedZone( name=hosted_zone_name, + arn=f"arn:aws:route53:::{hosted_zone_id}", id=hosted_zone_id, private_zone=True, region=AWS_REGION, diff --git a/providers/aws/services/route53/route53_service.py b/providers/aws/services/route53/route53_service.py index 8b78459e..5f695427 100644 --- a/providers/aws/services/route53/route53_service.py +++ b/providers/aws/services/route53/route53_service.py @@ -9,6 +9,7 @@ class Route53: def __init__(self, audit_info): self.service = "route53" self.session = audit_info.audit_session + self.audited_partition = audit_info.audited_partition self.client = self.session.client(self.service) self.region = get_region_global_service(audit_info) self.hosted_zones = {} @@ -32,6 +33,7 @@ class Route53: id=hosted_zone_id, name=hosted_zone_name, private_zone=private_zone, + arn=f"arn:{self.audited_partition}:route53:::{hosted_zone_id}", region=self.region, ) @@ -69,6 +71,7 @@ class LoggingConfig(BaseModel): class HostedZone(BaseModel): id: str + arn: str name: str private_zone: bool logging_config: LoggingConfig = None diff --git a/providers/aws/services/route53/route53_service_test.py b/providers/aws/services/route53/route53_service_test.py index e6dbc61f..96a4ef55 100644 --- a/providers/aws/services/route53/route53_service_test.py +++ b/providers/aws/services/route53/route53_service_test.py @@ -93,6 +93,10 @@ class Test_Route53_Service: assert len(route53.hosted_zones) == 1 assert route53.hosted_zones[hosted_zone_id] assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id + assert ( + route53.hosted_zones[hosted_zone_id].arn + == f"arn:aws:route53:::{hosted_zone_id}" + ) assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name assert route53.hosted_zones[hosted_zone_id].private_zone assert route53.hosted_zones[hosted_zone_id].logging_config @@ -131,6 +135,10 @@ class Test_Route53_Service: assert len(route53.hosted_zones) == 1 assert route53.hosted_zones[hosted_zone_id] assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id + assert ( + route53.hosted_zones[hosted_zone_id].arn + == f"arn:aws:route53:::{hosted_zone_id}" + ) assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name assert not route53.hosted_zones[hosted_zone_id].private_zone assert route53.hosted_zones[hosted_zone_id].logging_config @@ -159,6 +167,10 @@ class Test_Route53_Service: assert len(route53.hosted_zones) == 1 assert route53.hosted_zones[hosted_zone_id] assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id + assert ( + route53.hosted_zones[hosted_zone_id].arn + == f"arn:aws:route53:::{hosted_zone_id}" + ) assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name assert route53.hosted_zones[hosted_zone_id].private_zone assert not route53.hosted_zones[hosted_zone_id].logging_config @@ -183,6 +195,10 @@ class Test_Route53_Service: assert len(route53.hosted_zones) == 1 assert route53.hosted_zones[hosted_zone_id] assert route53.hosted_zones[hosted_zone_id].id == hosted_zone_id + assert ( + route53.hosted_zones[hosted_zone_id].arn + == f"arn:aws:route53:::{hosted_zone_id}" + ) assert route53.hosted_zones[hosted_zone_id].name == hosted_zone_name assert not route53.hosted_zones[hosted_zone_id].private_zone assert not route53.hosted_zones[hosted_zone_id].logging_config diff --git a/providers/aws/services/s3/s3_service.py b/providers/aws/services/s3/s3_service.py index 169971d1..44f395fe 100644 --- a/providers/aws/services/s3/s3_service.py +++ b/providers/aws/services/s3/s3_service.py @@ -182,9 +182,9 @@ class S3Control: def __init__(self, audit_info): self.service = "s3control" self.session = audit_info.audit_session - self.client = self.session.client(self.service) self.audited_account = audit_info.audited_account self.region = get_region_global_service(audit_info) + self.client = self.session.client(self.service, self.region) self.account_public_access_block = self.__get_public_access_block__() def __get_session__(self): diff --git a/providers/aws/services/shield/__init__.py b/providers/aws/services/shield/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/shield/check_extra7166 b/providers/aws/services/shield/check_extra7166 deleted file mode 100644 index 4a8430f5..00000000 --- a/providers/aws/services/shield/check_extra7166 +++ /dev/null @@ -1,50 +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_extra7166="7.166" -CHECK_TITLE_extra7166="[extra7166] Check if Elastic IP addresses with associations are protected by AWS Shield Advanced" -CHECK_SCORED_extra7166="NOT_SCORED" -CHECK_CIS_LEVEL_extra7166="EXTRA" -CHECK_SEVERITY_extra7166="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra7166="AwsEc2Eip" -CHECK_ALTERNATE_check7166="extra7166" -CHECK_SERVICENAME_extra7166="shield" -CHECK_RISK_extra7166='AWS Shield Advanced provides expanded DDoS attack protection for your resources' -CHECK_REMEDIATION_extra7166='Add as a protected resource in AWS Shield Advanced.' -CHECK_DOC_extra7166='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html' -CHECK_CAF_EPIC_extra7166='Infrastructure security' - -extra7166() { - if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then - CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn) - PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2) - ACCOUNT_ID=$(echo $CALLER_IDENTITY | cut -d: -f5) - for regx in $REGIONS; do - LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS=$($AWSCLI ec2 describe-addresses $PROFILE_OPT --region $regx --query 'Addresses[?AssociationId].AllocationId' --output text) - if [[ $LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS ]]; then - for elastic_ip in $LIST_OF_ELASTIC_IPS_WITH_ASSOCIATIONS; do - EIP_ARN="arn:${PARTITION}:ec2:${regx}:${ACCOUNT_ID}:eip-allocation/${elastic_ip}" - if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $EIP_ARN >/dev/null 2>&1; then - textPass "$regx: EIP $elastic_ip is protected by AWS Shield Advanced" "$regx" "$elastic_ip" - else - textFail "$regx: EIP $elastic_ip is not protected by AWS Shield Advanced" "$regx" "$elastic_ip" - fi - done - else - textInfo "$regx: no elastic IP addresses with assocations found" "$regx" - fi - done - else - textInfo "No AWS Shield Advanced subscription found. Skipping check." - fi -} diff --git a/providers/aws/services/shield/check_extra7167 b/providers/aws/services/shield/check_extra7167 deleted file mode 100644 index c349c352..00000000 --- a/providers/aws/services/shield/check_extra7167 +++ /dev/null @@ -1,46 +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_extra7167="7.167" -CHECK_TITLE_extra7167="[extra7167] Check if Cloudfront distributions are protected by AWS Shield Advanced" -CHECK_SCORED_extra7167="NOT_SCORED" -CHECK_CIS_LEVEL_extra7167="EXTRA" -CHECK_SEVERITY_extra7167="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra7167="AwsCloudFrontDistribution" -CHECK_ALTERNATE_check7167="extra7167" -CHECK_SERVICENAME_extra7167="shield" -CHECK_RISK_extra7167='AWS Shield Advanced provides expanded DDoS attack protection for your resources' -CHECK_REMEDIATION_extra7167='Add as a protected resource in AWS Shield Advanced.' -CHECK_DOC_extra7167='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html' -CHECK_CAF_EPIC_extra7167='Infrastructure security' - -extra7167() { - if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then - LIST_OF_CLOUDFRONT_DISTRIBUTIONS=$($AWSCLI cloudfront list-distributions $PROFILE_OPT --query 'DistributionList.Items[*].[Id,ARN]' --output text | grep -v None) - if [[ $LIST_OF_CLOUDFRONT_DISTRIBUTIONS ]]; then - while read -r distribution; do - DISTRIBUTION_ID=$(echo $distribution | awk '{ print $1; }') - DISTRIBUTION_ARN=$(echo $distribution | awk '{ print $2; }') - if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $DISTRIBUTION_ARN >/dev/null 2>&1; then - textPass "$REGION: Cloudfront distribution $DISTRIBUTION_ID is protected by AWS Shield Advanced" "$REGION" "$DISTRIBUTION_ID" - else - textFail "$REGION: Cloudfront distribution $DISTRIBUTION_ID is not protected by AWS Shield Advanced" "$REGION" "$DISTRIBUTION_ID" - fi - done <<<"$LIST_OF_CLOUDFRONT_DISTRIBUTIONS" - else - textInfo "$REGION: no Cloudfront distributions found" "$REGION" - fi - else - textInfo "No AWS Shield Advanced subscription found. Skipping check." - fi -} diff --git a/providers/aws/services/shield/check_extra7168 b/providers/aws/services/shield/check_extra7168 deleted file mode 100644 index e2f2514d..00000000 --- a/providers/aws/services/shield/check_extra7168 +++ /dev/null @@ -1,49 +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_extra7168="7.168" -CHECK_TITLE_extra7168="[extra7168] Check if Route53 hosted zones are protected by AWS Shield Advanced" -CHECK_SCORED_extra7168="NOT_SCORED" -CHECK_CIS_LEVEL_extra7168="EXTRA" -CHECK_SEVERITY_extra7168="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra7168="AwsRoute53Domain" -CHECK_ALTERNATE_check7168="extra7168" -CHECK_SERVICENAME_extra7168="shield" -CHECK_RISK_extra7168='AWS Shield Advanced provides expanded DDoS attack protection for your resources' -CHECK_REMEDIATION_extra7168='Add as a protected resource in AWS Shield Advanced.' -CHECK_DOC_extra7168='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html' -CHECK_CAF_EPIC_extra7168='Infrastructure security' - -extra7168() { - if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then - CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn) - PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2) - LIST_OF_ROUTE53_HOSTED_ZONES=$($AWSCLI route53 list-hosted-zones $PROFILE_OPT --query 'HostedZones[*].[Id,Name]' --output text) - if [[ $LIST_OF_ROUTE53_HOSTED_ZONES ]]; then - while read -r hosted_zone; do - HOSTED_ZONE_ID=$(echo $hosted_zone | awk '{ print $1; }') - HOSTED_ZONE_NAME=$(echo $hosted_zone | awk '{ print $2; }') - HOSTED_ZONE_ARN="arn:${PARTITION}:route53:::${HOSTED_ZONE_ID:1}" - if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $HOSTED_ZONE_ARN >/dev/null 2>&1; then - textPass "$REGION: Route53 Hosted Zone $HOSTED_ZONE_NAME is protected by AWS Shield Advanced" "$REGION" "$HOSTED_ZONE_NAME" - else - textFail "$REGION: Route53 Hosted Zone $HOSTED_ZONE_NAME is not protected by AWS Shield Advanced" "$REGION" "$HOSTED_ZONE_NAME" - fi - done <<<"$LIST_OF_ROUTE53_HOSTED_ZONES" - else - textInfo "$REGION: no Route53 hosted zones found" "$REGION" - fi - else - textInfo "No AWS Shield Advanced subscription found. Skipping check." - fi -} diff --git a/providers/aws/services/shield/check_extra7169 b/providers/aws/services/shield/check_extra7169 deleted file mode 100644 index 573c0415..00000000 --- a/providers/aws/services/shield/check_extra7169 +++ /dev/null @@ -1,46 +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_extra7169="7.169" -CHECK_TITLE_extra7169="[extra7169] Check if global accelerators are protected by AWS Shield Advanced" -CHECK_SCORED_extra7169="NOT_SCORED" -CHECK_CIS_LEVEL_extra7169="EXTRA" -CHECK_SEVERITY_extra7169="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra7169="AwsGlobalAccelerator" -CHECK_ALTERNATE_check7169="extra7169" -CHECK_SERVICENAME_extra7169="shield" -CHECK_RISK_extra7169='AWS Shield Advanced provides expanded DDoS attack protection for your resources' -CHECK_REMEDIATION_extra7169='Add as a protected resource in AWS Shield Advanced.' -CHECK_DOC_extra7169='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html' -CHECK_CAF_EPIC_extra7169='Infrastructure security' - -extra7169() { - if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then - LIST_OF_GLOBAL_ACCELERATORS=$($AWSCLI globalaccelerator list-accelerators --region us-west-2 $PROFILE_OPT --query 'Accelerators[?Enabled].[Name,AcceleratorArn]' --output text) - if [[ $LIST_OF_GLOBAL_ACCELERATORS ]]; then - while read -r accelerator; do - ACCELERATOR_NAME=$(echo $accelerator | awk '{ print $1; }') - ACCELERATOR_ARN=$(echo $accelerator | awk '{ print $2; }') - if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $ACCELERATOR_ARN >/dev/null 2>&1; then - textPass "$REGION: Global Accelerator $ACCELERATOR_NAME is protected by AWS Shield Advanced" "$REGION" "$ACCELERATOR_NAME" - else - textFail "$REGION: Global Accelerator $ACCELERATOR_NAME is not protected by AWS Shield Advanced" "$REGION" "$ACCELERATOR_NAME" - fi - done <<<"$LIST_OF_GLOBAL_ACCELERATORS" - else - textInfo "$REGION: no global accelerators found" "$REGION" - fi - else - textInfo "No AWS Shield Advanced subscription found. Skipping check." - fi -} diff --git a/providers/aws/services/shield/check_extra7171 b/providers/aws/services/shield/check_extra7171 deleted file mode 100644 index 61834e58..00000000 --- a/providers/aws/services/shield/check_extra7171 +++ /dev/null @@ -1,50 +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_extra7171="7.171" -CHECK_TITLE_extra7171="[extra7171] Check if classic load balancers are protected by AWS Shield Advanced" -CHECK_SCORED_extra7171="NOT_SCORED" -CHECK_CIS_LEVEL_extra7171="EXTRA" -CHECK_SEVERITY_extra7171="Medium" -CHECK_ASFF_RESOURCE_TYPE_extra7171="AwsElasticLoadBalancingLoadBalancer" -CHECK_ALTERNATE_check7171="extra7171" -CHECK_SERVICENAME_extra7171="shield" -CHECK_RISK_extra7171='AWS Shield Advanced provides expanded DDoS attack protection for your resources' -CHECK_REMEDIATION_extra7171='Add as a protected resource in AWS Shield Advanced.' -CHECK_DOC_extra7171='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html' -CHECK_CAF_EPIC_extra7171='Infrastructure security' - -extra7171() { - if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then - CALLER_IDENTITY=$($AWSCLI sts get-caller-identity $PROFILE_OPT --query Arn) - PARTITION=$(echo $CALLER_IDENTITY | cut -d: -f2) - ACCOUNT_ID=$(echo $CALLER_IDENTITY | cut -d: -f5) - for regx in $REGIONS; do - LIST_OF_CLASSIC_LOAD_BALANCERS=$($AWSCLI elb describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancerDescriptions[?Scheme == `internet-facing`].[LoadBalancerName]' --output text |grep -v '^None$') - if [[ $LIST_OF_CLASSIC_LOAD_BALANCERS ]]; then - for elb in $LIST_OF_CLASSIC_LOAD_BALANCERS; do - ELB_ARN="arn:${PARTITION}:elasticloadbalancing:${regx}:${ACCOUNT_ID}:loadbalancer/${elb}" - if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $ELB_ARN >/dev/null 2>&1; then - textPass "$regx: ELB $elb is protected by AWS Shield Advanced" "$regx" "$elb" - else - textFail "$regx: ELB $elb is not protected by AWS Shield Advanced" "$regx" "$elb" - fi - done - else - textInfo "$regx: no classic load balancers found" "$regx" - fi - done - else - textInfo "No AWS Shield Advanced subscription found. Skipping check." - fi -} diff --git a/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/__init__.py b/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/shield_advanced_protection_in_associated_elastic_ips.metadata.json b/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/shield_advanced_protection_in_associated_elastic_ips.metadata.json new file mode 100644 index 00000000..88f079f7 --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/shield_advanced_protection_in_associated_elastic_ips.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "shield_advanced_protection_in_associated_elastic_ips", + "CheckTitle": "Check if Elastic IP addresses with associations are protected by AWS Shield Advanced.", + "CheckType": [], + "ServiceName": "shield", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "AwsEc2Eip", + "Description": "Check if Elastic IP addresses with associations are protected by AWS Shield Advanced.", + "Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.", + "RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Add as a protected resource in AWS Shield Advanced.", + "Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/shield_advanced_protection_in_associated_elastic_ips.py b/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/shield_advanced_protection_in_associated_elastic_ips.py new file mode 100644 index 00000000..06049628 --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/shield_advanced_protection_in_associated_elastic_ips.py @@ -0,0 +1,26 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.shield.shield_client import shield_client +from providers.aws.services.ec2.ec2_client import ec2_client + + +class shield_advanced_protection_in_associated_elastic_ips(Check): + def execute(self): + findings = [] + if shield_client.enabled: + for elastic_ip in ec2_client.elastic_ips: + report = Check_Report(self.metadata) + report.region = shield_client.region + report.resource_id = elastic_ip.allocation_id + report.resource_arn = elastic_ip.arn + report.status = "FAIL" + report.status_extended = f"Elastic IP {elastic_ip.allocation_id} is not protected by AWS Shield Advanced" + + for protection in shield_client.protections.values(): + if elastic_ip.arn == protection.resource_arn: + report.status = "PASS" + report.status_extended = f"Elastic IP {elastic_ip.allocation_id} is protected by AWS Shield Advanced" + break + + findings.append(report) + + return findings diff --git a/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/shield_advanced_protection_in_associated_elastic_ips_test.py b/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/shield_advanced_protection_in_associated_elastic_ips_test.py new file mode 100644 index 00000000..c3cf0e27 --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_associated_elastic_ips/shield_advanced_protection_in_associated_elastic_ips_test.py @@ -0,0 +1,207 @@ +from unittest import mock + +from boto3 import client, session +from mock import patch +from moto import mock_ec2 +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.lib.audit_info.models import AWS_Audit_Info +from providers.aws.services.shield.shield_service import Protection + +AWS_REGION = "eu-west-1" + + +# 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 every AWS call using Boto3 and generate_regional_clients to have 1 client +@patch( + "providers.aws.services.accessanalyzer.accessanalyzer_service.generate_regional_clients", + new=mock_generate_regional_clients, +) +class Test_shield_advanced_protection_in_associated_elastic_ips: + # 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=DEFAULT_ACCOUNT_ID, + audited_user_id=None, + audited_partition="aws", + audited_identity_arn=None, + profile=None, + profile_region=AWS_REGION, + credentials=None, + assumed_role_info=None, + audited_regions=None, + organizations_metadata=None, + ) + return audit_info + + @mock_ec2 + def test_no_shield_not_active(self): + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + + from providers.aws.services.ec2.ec2_service import EC2 + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips.ec2_client", + new=EC2(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips import ( + shield_advanced_protection_in_associated_elastic_ips, + ) + + check = shield_advanced_protection_in_associated_elastic_ips() + result = check.execute() + + assert len(result) == 0 + + @mock_ec2 + def test_shield_enabled_ip_protected(self): + # EC2 Client + ec2_client = client("ec2", region_name=AWS_REGION) + resp = ec2_client.allocate_address(Domain="vpc", Address="127.38.43.222") + allocation_id = resp["AllocationId"] + elastic_ip_arn = f"arn:aws:ec2:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:eip-allocation/{allocation_id}" + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + protection_id = "test-protection" + shield_client.protections = { + protection_id: Protection( + id=protection_id, + name="", + resource_arn=elastic_ip_arn, + protection_arn="", + region=AWS_REGION, + ) + } + + from providers.aws.services.ec2.ec2_service import EC2 + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips.ec2_client", + new=EC2(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips import ( + shield_advanced_protection_in_associated_elastic_ips, + ) + + check = shield_advanced_protection_in_associated_elastic_ips() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == allocation_id + assert result[0].resource_arn == elastic_ip_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Elastic IP {allocation_id} is protected by AWS Shield Advanced" + ) + + @mock_ec2 + def test_shield_enabled_ip_not_protected(self): + # EC2 Client + ec2_client = client("ec2", region_name=AWS_REGION) + resp = ec2_client.allocate_address(Domain="vpc", Address="127.38.43.222") + allocation_id = resp["AllocationId"] + elastic_ip_arn = f"arn:aws:ec2:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:eip-allocation/{allocation_id}" + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + shield_client.protections = {} + + from providers.aws.services.ec2.ec2_service import EC2 + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips.ec2_client", + new=EC2(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips import ( + shield_advanced_protection_in_associated_elastic_ips, + ) + + check = shield_advanced_protection_in_associated_elastic_ips() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == allocation_id + assert result[0].resource_arn == elastic_ip_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Elastic IP {allocation_id} is not protected by AWS Shield Advanced" + ) + + @mock_ec2 + def test_shield_disabled_ip_not_protected(self): + # EC2 Client + ec2_client = client("ec2", region_name=AWS_REGION) + resp = ec2_client.allocate_address(Domain="vpc", Address="127.38.43.222") + allocation_id = resp["AllocationId"] + _ = f"arn:aws:ec2:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:eip-allocation/{allocation_id}" + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + shield_client.region = AWS_REGION + shield_client.protections = {} + + from providers.aws.services.ec2.ec2_service import EC2 + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips.ec2_client", + new=EC2(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_associated_elastic_ips.shield_advanced_protection_in_associated_elastic_ips import ( + shield_advanced_protection_in_associated_elastic_ips, + ) + + check = shield_advanced_protection_in_associated_elastic_ips() + result = check.execute() + + assert len(result) == 0 diff --git a/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/__init__.py b/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/shield_advanced_protection_in_classic_load_balancers.metadata.json b/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/shield_advanced_protection_in_classic_load_balancers.metadata.json new file mode 100644 index 00000000..6513d48a --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/shield_advanced_protection_in_classic_load_balancers.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "shield_advanced_protection_in_associated_elastic_ips", + "CheckTitle": "Check if Classic Load Balancers are protected by AWS Shield Advanced.", + "CheckType": [], + "ServiceName": "shield", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "AwsElasticLoadBalancingLoadBalancer", + "Description": "Check if Classic Load Balancers are protected by AWS Shield Advanced.", + "Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.", + "RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Add as a protected resource in AWS Shield Advanced.", + "Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/shield_advanced_protection_in_classic_load_balancers.py b/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/shield_advanced_protection_in_classic_load_balancers.py new file mode 100644 index 00000000..e69b3b25 --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/shield_advanced_protection_in_classic_load_balancers.py @@ -0,0 +1,30 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.shield.shield_client import shield_client +from providers.aws.services.elb.elb_client import elb_client + + +class shield_advanced_protection_in_classic_load_balancers(Check): + def execute(self): + findings = [] + if shield_client.enabled: + for elb in elb_client.loadbalancers: + report = Check_Report(self.metadata) + report.region = shield_client.region + report.resource_id = elb.name + report.resource_arn = elb.arn + report.status = "FAIL" + report.status_extended = ( + f"ELB {elb.name} is not protected by AWS Shield Advanced" + ) + + for protection in shield_client.protections.values(): + if elb.arn == protection.resource_arn: + report.status = "PASS" + report.status_extended = ( + f"ELB {elb.name} is protected by AWS Shield Advanced" + ) + break + + findings.append(report) + + return findings diff --git a/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/shield_advanced_protection_in_classic_load_balancers_test.py b/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/shield_advanced_protection_in_classic_load_balancers_test.py new file mode 100644 index 00000000..175c42d0 --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_classic_load_balancers/shield_advanced_protection_in_classic_load_balancers_test.py @@ -0,0 +1,240 @@ +from unittest import mock + +from boto3 import client, resource, session +from moto import mock_ec2, mock_elb +from moto.core import DEFAULT_ACCOUNT_ID + +from providers.aws.lib.audit_info.models import AWS_Audit_Info +from providers.aws.services.shield.shield_service import Protection + +AWS_REGION = "eu-west-1" + + +class Test_shield_advanced_protection_in_classic_load_balancers: + # 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=DEFAULT_ACCOUNT_ID, + audited_user_id=None, + audited_partition="aws", + audited_identity_arn=None, + profile=None, + profile_region=AWS_REGION, + credentials=None, + assumed_role_info=None, + audited_regions=None, + organizations_metadata=None, + ) + return audit_info + + @mock_elb + @mock_ec2 + def test_no_shield_not_active(self): + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + + from providers.aws.services.elb.elb_service import ELB + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers.elb_client", + new=ELB(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers import ( + shield_advanced_protection_in_classic_load_balancers, + ) + + check = shield_advanced_protection_in_classic_load_balancers() + result = check.execute() + + assert len(result) == 0 + + @mock_ec2 + @mock_elb + def test_shield_enabled_elb_protected(self): + # ELB Client + elb = client("elb", region_name=AWS_REGION) + ec2 = resource("ec2", region_name=AWS_REGION) + + security_group = ec2.create_security_group( + GroupName="sg01", Description="Test security group sg01" + ) + elb_name = "my-lb" + elb.create_load_balancer( + LoadBalancerName=elb_name, + Listeners=[ + {"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080}, + {"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000}, + ], + AvailabilityZones=[f"{AWS_REGION}a"], + Scheme="internet-facing", + SecurityGroups=[security_group.id], + ) + elb_arn = f"arn:aws:elasticloadbalancing:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:loadbalancer/{elb_name}" + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + protection_id = "test-protection" + shield_client.protections = { + protection_id: Protection( + id=protection_id, + name="", + resource_arn=elb_arn, + protection_arn="", + region=AWS_REGION, + ) + } + + from providers.aws.services.elb.elb_service import ELB + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers.elb_client", + new=ELB(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers import ( + shield_advanced_protection_in_classic_load_balancers, + ) + + check = shield_advanced_protection_in_classic_load_balancers() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == elb_name + assert result[0].resource_arn == elb_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"ELB {elb_name} is protected by AWS Shield Advanced" + ) + + @mock_elb + @mock_ec2 + def test_shield_enabled_elb_not_protected(self): + # ELB Client + elb = client("elb", region_name=AWS_REGION) + ec2 = resource("ec2", region_name=AWS_REGION) + + security_group = ec2.create_security_group( + GroupName="sg01", Description="Test security group sg01" + ) + elb_name = "my-lb" + elb.create_load_balancer( + LoadBalancerName=elb_name, + Listeners=[ + {"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080}, + {"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000}, + ], + AvailabilityZones=[f"{AWS_REGION}a"], + Scheme="internet-facing", + SecurityGroups=[security_group.id], + ) + elb_arn = f"arn:aws:elasticloadbalancing:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:loadbalancer/{elb_name}" + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + shield_client.protections = {} + + from providers.aws.services.elb.elb_service import ELB + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers.elb_client", + new=ELB(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers import ( + shield_advanced_protection_in_classic_load_balancers, + ) + + check = shield_advanced_protection_in_classic_load_balancers() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == elb_name + assert result[0].resource_arn == elb_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"ELB {elb_name} is not protected by AWS Shield Advanced" + ) + + @mock_elb + @mock_ec2 + def test_shield_disabled_elb_not_protected(self): + # ELB Client + elb = client("elb", region_name=AWS_REGION) + ec2 = resource("ec2", region_name=AWS_REGION) + + security_group = ec2.create_security_group( + GroupName="sg01", Description="Test security group sg01" + ) + elb_name = "my-lb" + elb.create_load_balancer( + LoadBalancerName=elb_name, + Listeners=[ + {"Protocol": "tcp", "LoadBalancerPort": 80, "InstancePort": 8080}, + {"Protocol": "http", "LoadBalancerPort": 81, "InstancePort": 9000}, + ], + AvailabilityZones=[f"{AWS_REGION}a"], + Scheme="internet-facing", + SecurityGroups=[security_group.id], + ) + _ = f"arn:aws:elasticloadbalancing:{AWS_REGION}:{DEFAULT_ACCOUNT_ID}:loadbalancer/{elb_name}" + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + shield_client.region = AWS_REGION + shield_client.protections = {} + + from providers.aws.services.elb.elb_service import ELB + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers.elb_client", + new=ELB(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_classic_load_balancers.shield_advanced_protection_in_classic_load_balancers import ( + shield_advanced_protection_in_classic_load_balancers, + ) + + check = shield_advanced_protection_in_classic_load_balancers() + result = check.execute() + + assert len(result) == 0 diff --git a/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/__init__.py b/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/shield_advanced_protection_in_cloudfront_distributions.metadata.json b/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/shield_advanced_protection_in_cloudfront_distributions.metadata.json new file mode 100644 index 00000000..3638930f --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/shield_advanced_protection_in_cloudfront_distributions.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "shield_advanced_protection_in_cloudfront_distributions", + "CheckTitle": "Check if Cloudfront distributions are protected by AWS Shield Advanced.", + "CheckType": [], + "ServiceName": "shield", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "AwsCloudFrontDistribution", + "Description": "Check if Cloudfront distributions are protected by AWS Shield Advanced.", + "Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.", + "RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Add as a protected resource in AWS Shield Advanced.", + "Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/shield_advanced_protection_in_cloudfront_distributions.py b/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/shield_advanced_protection_in_cloudfront_distributions.py new file mode 100644 index 00000000..7e3bf5af --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/shield_advanced_protection_in_cloudfront_distributions.py @@ -0,0 +1,26 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.shield.shield_client import shield_client +from providers.aws.services.cloudfront.cloudfront_client import cloudfront_client + + +class shield_advanced_protection_in_cloudfront_distributions(Check): + def execute(self): + findings = [] + if shield_client.enabled: + for distribution in cloudfront_client.distributions.values(): + report = Check_Report(self.metadata) + report.region = shield_client.region + report.resource_id = distribution.id + report.resource_arn = distribution.arn + report.status = "FAIL" + report.status_extended = f"CloudFront distribution {distribution.id} is not protected by AWS Shield Advanced" + + for protection in shield_client.protections.values(): + if distribution.arn == protection.resource_arn: + report.status = "PASS" + report.status_extended = f"CloudFront distribution {distribution.id} is protected by AWS Shield Advanced" + break + + findings.append(report) + + return findings diff --git a/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/shield_advanced_protection_in_cloudfront_distributions_test.py b/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/shield_advanced_protection_in_cloudfront_distributions_test.py new file mode 100644 index 00000000..2f789935 --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_cloudfront_distributions/shield_advanced_protection_in_cloudfront_distributions_test.py @@ -0,0 +1,165 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID +from providers.aws.services.cloudfront.cloudfront_service import Distribution +from providers.aws.services.shield.shield_service import Protection + +AWS_REGION = "eu-west-1" + + +class Test_shield_advanced_protection_in_cloudfront_distributions: + def test_no_shield_not_active(self): + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + # CloudFront Client + cloudfront_client = mock.MagicMock + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_cloudfront_distributions.shield_advanced_protection_in_cloudfront_distributions import ( + shield_advanced_protection_in_cloudfront_distributions, + ) + + check = shield_advanced_protection_in_cloudfront_distributions() + result = check.execute() + + assert len(result) == 0 + + def test_shield_enabled_cloudfront_protected(self): + # CloudFront Client + cloudfront_client = mock.MagicMock + distribution_id = "EDFDVBD632BHDS5" + distribution_arn = ( + f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{distribution_id}" + ) + cloudfront_client.distributions = { + distribution_id: Distribution( + arn=distribution_arn, id=distribution_id, region=AWS_REGION, origins=[] + ) + } + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + protection_id = "test-protection" + shield_client.protections = { + protection_id: Protection( + id=protection_id, + name="", + resource_arn=distribution_arn, + protection_arn="", + region=AWS_REGION, + ) + } + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_cloudfront_distributions.shield_advanced_protection_in_cloudfront_distributions import ( + shield_advanced_protection_in_cloudfront_distributions, + ) + + check = shield_advanced_protection_in_cloudfront_distributions() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == distribution_id + assert result[0].resource_arn == distribution_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"CloudFront distribution {distribution_id} is protected by AWS Shield Advanced" + ) + + def test_shield_enabled_cloudfront_not_protected(self): + # CloudFront Client + cloudfront_client = mock.MagicMock + distribution_id = "EDFDVBD632BHDS5" + distribution_arn = ( + f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{distribution_id}" + ) + cloudfront_client.distributions = { + distribution_id: Distribution( + arn=distribution_arn, id=distribution_id, region=AWS_REGION, origins=[] + ) + } + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + shield_client.protections = {} + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_cloudfront_distributions.shield_advanced_protection_in_cloudfront_distributions import ( + shield_advanced_protection_in_cloudfront_distributions, + ) + + check = shield_advanced_protection_in_cloudfront_distributions() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == distribution_id + assert result[0].resource_arn == distribution_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"CloudFront distribution {distribution_id} is not protected by AWS Shield Advanced" + ) + + def test_shield_disabled_cloudfront_not_protected(self): + # CloudFront Client + cloudfront_client = mock.MagicMock + distribution_id = "EDFDVBD632BHDS5" + distribution_arn = ( + f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{distribution_id}" + ) + cloudfront_client.distributions = { + distribution_id: Distribution( + arn=distribution_arn, id=distribution_id, region=AWS_REGION, origins=[] + ) + } + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + shield_client.region = AWS_REGION + shield_client.protections = {} + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.cloudfront.cloudfront_service.CloudFront", + new=cloudfront_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_cloudfront_distributions.shield_advanced_protection_in_cloudfront_distributions import ( + shield_advanced_protection_in_cloudfront_distributions, + ) + + check = shield_advanced_protection_in_cloudfront_distributions() + result = check.execute() + + assert len(result) == 0 diff --git a/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/__init__.py b/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/shield_advanced_protection_in_global_accelerators.metadata.json b/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/shield_advanced_protection_in_global_accelerators.metadata.json new file mode 100644 index 00000000..6ed9608a --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/shield_advanced_protection_in_global_accelerators.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "shield_advanced_protection_in_global_accelerators", + "CheckTitle": "Check if Global Accelerators are protected by AWS Shield Advanced.", + "CheckType": [], + "ServiceName": "shield", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "AwsGlobalAccelerator", + "Description": "Check if Global Accelerators are protected by AWS Shield Advanced.", + "Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.", + "RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Add as a protected resource in AWS Shield Advanced.", + "Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/shield_advanced_protection_in_global_accelerators.py b/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/shield_advanced_protection_in_global_accelerators.py new file mode 100644 index 00000000..c602904e --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/shield_advanced_protection_in_global_accelerators.py @@ -0,0 +1,28 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.shield.shield_client import shield_client +from providers.aws.services.globalaccelerator.globalaccelerator_client import ( + globalaccelerator_client, +) + + +class shield_advanced_protection_in_global_accelerators(Check): + def execute(self): + findings = [] + if shield_client.enabled: + for accelerator in globalaccelerator_client.accelerators.values(): + report = Check_Report(self.metadata) + report.region = shield_client.region + report.resource_id = accelerator.name + report.resource_arn = accelerator.arn + report.status = "FAIL" + report.status_extended = f"Global Accelerator {accelerator.name} is not protected by AWS Shield Advanced" + + for protection in shield_client.protections.values(): + if accelerator.arn == protection.resource_arn: + report.status = "PASS" + report.status_extended = f"Global Accelerator {accelerator.name} is protected by AWS Shield Advanced" + break + + findings.append(report) + + return findings diff --git a/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/shield_advanced_protection_in_global_accelerators_test.py b/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/shield_advanced_protection_in_global_accelerators_test.py new file mode 100644 index 00000000..f3a4842e --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_global_accelerators/shield_advanced_protection_in_global_accelerators_test.py @@ -0,0 +1,173 @@ +from unittest import mock + +from moto.core import DEFAULT_ACCOUNT_ID +from providers.aws.services.globalaccelerator.globalaccelerator_service import ( + Accelerator, +) +from providers.aws.services.shield.shield_service import Protection + +AWS_REGION = "eu-west-1" + + +class Test_shield_advanced_protection_in_global_accelerators: + def test_no_shield_not_active(self): + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + # GlobalAccelerator Client + globalaccelerator_client = mock.MagicMock + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.globalaccelerator.globalaccelerator_service.GlobalAccelerator", + new=globalaccelerator_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_global_accelerators.shield_advanced_protection_in_global_accelerators import ( + shield_advanced_protection_in_global_accelerators, + ) + + check = shield_advanced_protection_in_global_accelerators() + result = check.execute() + + assert len(result) == 0 + + def test_shield_enabled_globalaccelerator_protected(self): + # GlobalAccelerator Client + globalaccelerator_client = mock.MagicMock + accelerator_name = "1234abcd-abcd-1234-abcd-1234abcdefgh" + accelerator_id = "1234abcd-abcd-1234-abcd-1234abcdefgh" + accelerator_arn = f"arn:aws:globalaccelerator::{DEFAULT_ACCOUNT_ID}:accelerator/{accelerator_id}" + globalaccelerator_client.accelerators = { + accelerator_name: Accelerator( + arn=accelerator_arn, + name=accelerator_name, + region=AWS_REGION, + enabled=True, + ) + } + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + protection_id = "test-protection" + shield_client.protections = { + protection_id: Protection( + id=protection_id, + name="", + resource_arn=accelerator_arn, + protection_arn="", + region=AWS_REGION, + ) + } + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.globalaccelerator.globalaccelerator_service.GlobalAccelerator", + new=globalaccelerator_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_global_accelerators.shield_advanced_protection_in_global_accelerators import ( + shield_advanced_protection_in_global_accelerators, + ) + + check = shield_advanced_protection_in_global_accelerators() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == accelerator_id + assert result[0].resource_arn == accelerator_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Global Accelerator {accelerator_id} is protected by AWS Shield Advanced" + ) + + def test_shield_enabled_globalaccelerator_not_protected(self): + # GlobalAccelerator Client + globalaccelerator_client = mock.MagicMock + accelerator_name = "1234abcd-abcd-1234-abcd-1234abcdefgh" + accelerator_id = "1234abcd-abcd-1234-abcd-1234abcdefgh" + accelerator_arn = f"arn:aws:globalaccelerator::{DEFAULT_ACCOUNT_ID}:accelerator/{accelerator_id}" + globalaccelerator_client.accelerators = { + accelerator_name: Accelerator( + arn=accelerator_arn, + name=accelerator_name, + region=AWS_REGION, + enabled=True, + ) + } + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + shield_client.protections = {} + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.globalaccelerator.globalaccelerator_service.GlobalAccelerator", + new=globalaccelerator_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_global_accelerators.shield_advanced_protection_in_global_accelerators import ( + shield_advanced_protection_in_global_accelerators, + ) + + check = shield_advanced_protection_in_global_accelerators() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == accelerator_id + assert result[0].resource_arn == accelerator_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Global Accelerator {accelerator_id} is not protected by AWS Shield Advanced" + ) + + def test_shield_disabled_globalaccelerator_not_protected(self): + # GlobalAccelerator Client + globalaccelerator_client = mock.MagicMock + accelerator_name = "1234abcd-abcd-1234-abcd-1234abcdefgh" + accelerator_id = "1234abcd-abcd-1234-abcd-1234abcdefgh" + accelerator_arn = f"arn:aws:globalaccelerator::{DEFAULT_ACCOUNT_ID}:accelerator/{accelerator_id}" + globalaccelerator_client.accelerators = { + accelerator_name: Accelerator( + arn=accelerator_arn, + name=accelerator_name, + region=AWS_REGION, + enabled=True, + ) + } + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + shield_client.region = AWS_REGION + shield_client.protections = {} + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.globalaccelerator.globalaccelerator_service.GlobalAccelerator", + new=globalaccelerator_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_global_accelerators.shield_advanced_protection_in_global_accelerators import ( + shield_advanced_protection_in_global_accelerators, + ) + + check = shield_advanced_protection_in_global_accelerators() + result = check.execute() + + assert len(result) == 0 diff --git a/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/__init__.py b/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/shield_advanced_protection_in_internet_facing_load_balancers.metadata.json b/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/shield_advanced_protection_in_internet_facing_load_balancers.metadata.json new file mode 100644 index 00000000..0189ca74 --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/shield_advanced_protection_in_internet_facing_load_balancers.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "shield_advanced_protection_in_internet_facing_load_balancers", + "CheckTitle": "Check if internet-facing Application Load Balancers are protected by AWS Shield Advanced.", + "CheckType": [], + "ServiceName": "shield", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "AwsElasticLoadBalancingV2LoadBalancer", + "Description": "Check if internet-facing Application Load Balancers are protected by AWS Shield Advanced.", + "Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.", + "RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Add as a protected resource in AWS Shield Advanced.", + "Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/shield_advanced_protection_in_internet_facing_load_balancers.py b/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/shield_advanced_protection_in_internet_facing_load_balancers.py new file mode 100644 index 00000000..6c8fa0ee --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/shield_advanced_protection_in_internet_facing_load_balancers.py @@ -0,0 +1,29 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.shield.shield_client import shield_client +from providers.aws.services.elbv2.elbv2_client import ( + elbv2_client, +) + + +class shield_advanced_protection_in_internet_facing_load_balancers(Check): + def execute(self): + findings = [] + if shield_client.enabled: + for elbv2 in elbv2_client.loadbalancersv2: + if elbv2.type == "application" and elbv2.scheme == "internet-facing": + report = Check_Report(self.metadata) + report.region = shield_client.region + report.resource_id = elbv2.name + report.resource_arn = elbv2.arn + report.status = "FAIL" + report.status_extended = f"ELBv2 ALB {elbv2.name} is not protected by AWS Shield Advanced" + + for protection in shield_client.protections.values(): + if elbv2.arn == protection.resource_arn: + report.status = "PASS" + report.status_extended = f"ELBv2 ALB {elbv2.name} is protected by AWS Shield Advanced" + break + + findings.append(report) + + return findings diff --git a/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/shield_advanced_protection_in_internet_facing_load_balancers_test.py b/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/shield_advanced_protection_in_internet_facing_load_balancers_test.py new file mode 100644 index 00000000..0e4740ad --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_internet_facing_load_balancers/shield_advanced_protection_in_internet_facing_load_balancers_test.py @@ -0,0 +1,337 @@ +from unittest import mock + +from boto3 import client, resource, session +from mock import patch +from moto import mock_ec2, mock_elbv2 +from moto.core import DEFAULT_ACCOUNT_ID as AWS_ACCOUNT_NUMBER + +from providers.aws.lib.audit_info.models import AWS_Audit_Info +from providers.aws.services.shield.shield_service import Protection + +AWS_REGION = "eu-west-1" + + +# 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 every AWS call using Boto3 and generate_regional_clients to have 1 client +@patch( + "providers.aws.services.accessanalyzer.accessanalyzer_service.generate_regional_clients", + new=mock_generate_regional_clients, +) +class Test_shield_advanced_protection_in_internet_facing_load_balancers: + # 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=AWS_ACCOUNT_NUMBER, + audited_user_id=None, + audited_partition="aws", + audited_identity_arn=None, + profile=None, + profile_region=AWS_REGION, + credentials=None, + assumed_role_info=None, + audited_regions=None, + organizations_metadata=None, + ) + return audit_info + + @mock_ec2 + @mock_elbv2 + def test_no_shield_not_active(self): + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + + from providers.aws.services.elbv2.elbv2_service import ELBv2 + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers.elbv2_client", + new=ELBv2(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers import ( + shield_advanced_protection_in_internet_facing_load_balancers, + ) + + check = shield_advanced_protection_in_internet_facing_load_balancers() + result = check.execute() + + assert len(result) == 0 + + @mock_ec2 + @mock_elbv2 + def test_shield_enabled_elbv2_internet_facing_protected(self): + # ELBv2 Client + conn = client("elbv2", region_name=AWS_REGION) + ec2 = resource("ec2", region_name=AWS_REGION) + + security_group = ec2.create_security_group( + GroupName="a-security-group", Description="First One" + ) + vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default") + subnet1 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock="172.28.7.192/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + subnet2 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock="172.28.7.0/26", + AvailabilityZone=f"{AWS_REGION}b", + ) + lb_name = "my-lb" + lb = conn.create_load_balancer( + Name=lb_name, + Subnets=[subnet1.id, subnet2.id], + SecurityGroups=[security_group.id], + Scheme="internet-facing", + Type="application", + )["LoadBalancers"][0] + lb_arn = lb["LoadBalancerArn"] + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + protection_id = "test-protection" + shield_client.protections = { + protection_id: Protection( + id=protection_id, + name="", + resource_arn=lb_arn, + protection_arn="", + region=AWS_REGION, + ) + } + + from providers.aws.services.elbv2.elbv2_service import ELBv2 + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers.elbv2_client", + new=ELBv2(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers import ( + shield_advanced_protection_in_internet_facing_load_balancers, + ) + + check = shield_advanced_protection_in_internet_facing_load_balancers() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == lb_name + assert result[0].resource_arn == lb["LoadBalancerArn"] + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"ELBv2 ALB {lb_name} is protected by AWS Shield Advanced" + ) + + @mock_ec2 + @mock_elbv2 + def test_shield_enabled_elbv2_internal_protected(self): + # ELBv2 Client + conn = client("elbv2", region_name=AWS_REGION) + ec2 = resource("ec2", region_name=AWS_REGION) + + security_group = ec2.create_security_group( + GroupName="a-security-group", Description="First One" + ) + vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default") + subnet1 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock="172.28.7.192/26", + AvailabilityZone=f"{AWS_REGION}a", + ) + subnet2 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock="172.28.7.0/26", + AvailabilityZone=f"{AWS_REGION}b", + ) + lb_name = "my-lb" + lb = conn.create_load_balancer( + Name=lb_name, + Subnets=[subnet1.id, subnet2.id], + SecurityGroups=[security_group.id], + Scheme="internal", + Type="application", + )["LoadBalancers"][0] + lb_arn = lb["LoadBalancerArn"] + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + protection_id = "test-protection" + shield_client.protections = { + protection_id: Protection( + id=protection_id, + name="", + resource_arn=lb_arn, + protection_arn="", + region=AWS_REGION, + ) + } + + from providers.aws.services.elbv2.elbv2_service import ELBv2 + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers.elbv2_client", + new=ELBv2(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers import ( + shield_advanced_protection_in_internet_facing_load_balancers, + ) + + check = shield_advanced_protection_in_internet_facing_load_balancers() + result = check.execute() + + assert len(result) == 0 + + @mock_ec2 + @mock_elbv2 + def test_shield_enabled_elbv2_internet_facing_not_protected(self): + # ELBv2 Client + conn = client("elbv2", region_name=AWS_REGION) + ec2 = resource("ec2", region_name=AWS_REGION) + + security_group = ec2.create_security_group( + GroupName="a-security-group", Description="First One" + ) + vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default") + subnet1 = ec2.create_subnet( + VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a" + ) + subnet2 = ec2.create_subnet( + VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b" + ) + lb_name = "my-lb" + lb = conn.create_load_balancer( + Name=lb_name, + Subnets=[subnet1.id, subnet2.id], + SecurityGroups=[security_group.id], + Scheme="internet-facing", + Type="application", + )["LoadBalancers"][0] + lb_arn = lb["LoadBalancerArn"] + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + shield_client.protections = {} + + from providers.aws.services.elbv2.elbv2_service import ELBv2 + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers.elbv2_client", + new=ELBv2(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers import ( + shield_advanced_protection_in_internet_facing_load_balancers, + ) + + check = shield_advanced_protection_in_internet_facing_load_balancers() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == lb_name + assert result[0].resource_arn == lb_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"ELBv2 ALB {lb_name} is not protected by AWS Shield Advanced" + ) + + @mock_ec2 + @mock_elbv2 + def test_shield_disabled_elbv2_internet_facing_not_protected(self): + # ELBv2 Client + conn = client("elbv2", region_name=AWS_REGION) + ec2 = resource("ec2", region_name=AWS_REGION) + + security_group = ec2.create_security_group( + GroupName="a-security-group", Description="First One" + ) + vpc = ec2.create_vpc(CidrBlock="172.28.7.0/24", InstanceTenancy="default") + subnet1 = ec2.create_subnet( + VpcId=vpc.id, CidrBlock="172.28.7.192/26", AvailabilityZone=f"{AWS_REGION}a" + ) + subnet2 = ec2.create_subnet( + VpcId=vpc.id, CidrBlock="172.28.7.0/26", AvailabilityZone=f"{AWS_REGION}b" + ) + lb_name = "my-lb" + lb = conn.create_load_balancer( + Name=lb_name, + Subnets=[subnet1.id, subnet2.id], + SecurityGroups=[security_group.id], + Scheme="internal", + Type="application", + )["LoadBalancers"][0] + _ = lb["LoadBalancerArn"] + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + shield_client.region = AWS_REGION + shield_client.protections = {} + + from providers.aws.services.elbv2.elbv2_service import ELBv2 + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.lib.audit_info.audit_info.current_audit_info", + new=self.set_mocked_audit_info(), + ), mock.patch( + "providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers.elbv2_client", + new=ELBv2(self.set_mocked_audit_info()), + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_internet_facing_load_balancers.shield_advanced_protection_in_internet_facing_load_balancers import ( + shield_advanced_protection_in_internet_facing_load_balancers, + ) + + check = shield_advanced_protection_in_internet_facing_load_balancers() + result = check.execute() + + assert len(result) == 0 diff --git a/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/__init__.py b/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/shield_advanced_protection_in_route53_hosted_zones.metadata.json b/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/shield_advanced_protection_in_route53_hosted_zones.metadata.json new file mode 100644 index 00000000..aaa0561c --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/shield_advanced_protection_in_route53_hosted_zones.metadata.json @@ -0,0 +1,35 @@ +{ + "Provider": "aws", + "CheckID": "shield_advanced_protection_in_route53_hosted_zones", + "CheckTitle": "Check if Route53 hosted zones are protected by AWS Shield Advanced.", + "CheckType": [], + "ServiceName": "shield", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "AwsRoute53Domain", + "Description": "Check if Route53 hosted zones are protected by AWS Shield Advanced.", + "Risk": "AWS Shield Advanced provides expanded DDoS attack protection for your resources.", + "RelatedUrl": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "Add as a protected resource in AWS Shield Advanced.", + "Url": "https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "", + "Compliance": [] +} \ No newline at end of file diff --git a/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/shield_advanced_protection_in_route53_hosted_zones.py b/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/shield_advanced_protection_in_route53_hosted_zones.py new file mode 100644 index 00000000..6e60e3ee --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/shield_advanced_protection_in_route53_hosted_zones.py @@ -0,0 +1,28 @@ +from lib.check.models import Check, Check_Report +from providers.aws.services.shield.shield_client import shield_client +from providers.aws.services.route53.route53_client import ( + route53_client, +) + + +class shield_advanced_protection_in_route53_hosted_zones(Check): + def execute(self): + findings = [] + if shield_client.enabled: + for hosted_zone in route53_client.hosted_zones.values(): + report = Check_Report(self.metadata) + report.region = shield_client.region + report.resource_id = hosted_zone.id + report.resource_arn = hosted_zone.arn + report.status = "FAIL" + report.status_extended = f"Route53 Hosted Zone {hosted_zone.id} is not protected by AWS Shield Advanced" + + for protection in shield_client.protections.values(): + if hosted_zone.arn == protection.resource_arn: + report.status = "PASS" + report.status_extended = f"Route53 Hosted Zone {hosted_zone.id} is protected by AWS Shield Advanced" + break + + findings.append(report) + + return findings diff --git a/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/shield_advanced_protection_in_route53_hosted_zones_test.py b/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/shield_advanced_protection_in_route53_hosted_zones_test.py new file mode 100644 index 00000000..6f326eb9 --- /dev/null +++ b/providers/aws/services/shield/shield_advanced_protection_in_route53_hosted_zones/shield_advanced_protection_in_route53_hosted_zones_test.py @@ -0,0 +1,181 @@ +from unittest import mock + +from providers.aws.services.route53.route53_service import ( + HostedZone, +) +from providers.aws.services.shield.shield_service import Protection + +AWS_REGION = "eu-west-1" + + +class Test_shield_advanced_protection_in_route53_hosted_zones: + def test_no_shield_not_active(self): + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + # Route53 Client + route53_client = mock.MagicMock + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.route53.route53_service.Route53", + new=route53_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones import ( + shield_advanced_protection_in_route53_hosted_zones, + ) + + check = shield_advanced_protection_in_route53_hosted_zones() + result = check.execute() + + assert len(result) == 0 + + def test_shield_enabled_route53_hosted_zone_protected(self): + # Route53 Client + route53_client = mock.MagicMock + hosted_zone_id = "ABCDEF12345678" + hosted_zone_arn = f"arn:aws:route53:::hostedzone/{hosted_zone_id}" + hosted_zone_name = "test-hosted-zone" + + route53_client.hosted_zones = { + hosted_zone_id: HostedZone( + id=hosted_zone_id, + arn=hosted_zone_arn, + name=hosted_zone_name, + hosted_zone_name=hosted_zone_name, + private_zone=False, + region=AWS_REGION, + ) + } + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + protection_id = "test-protection" + shield_client.protections = { + protection_id: Protection( + id=protection_id, + name="", + resource_arn=hosted_zone_arn, + protection_arn="", + region=AWS_REGION, + ) + } + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.route53.route53_service.Route53", + new=route53_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones import ( + shield_advanced_protection_in_route53_hosted_zones, + ) + + check = shield_advanced_protection_in_route53_hosted_zones() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == hosted_zone_id + assert result[0].resource_arn == hosted_zone_arn + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Route53 Hosted Zone {hosted_zone_id} is protected by AWS Shield Advanced" + ) + + def test_shield_enabled_route53_hosted_zone_not_protected(self): + # Route53 Client + route53_client = mock.MagicMock + hosted_zone_id = "ABCDEF12345678" + hosted_zone_arn = f"arn:aws:route53:::hostedzone/{hosted_zone_id}" + hosted_zone_name = "test-hosted-zone" + + route53_client.hosted_zones = { + hosted_zone_id: HostedZone( + id=hosted_zone_id, + arn=hosted_zone_arn, + name=hosted_zone_name, + hosted_zone_name=hosted_zone_name, + private_zone=False, + region=AWS_REGION, + ) + } + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = True + shield_client.region = AWS_REGION + shield_client.protections = {} + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.route53.route53_service.Route53", + new=route53_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones import ( + shield_advanced_protection_in_route53_hosted_zones, + ) + + check = shield_advanced_protection_in_route53_hosted_zones() + result = check.execute() + + assert len(result) == 1 + assert result[0].region == AWS_REGION + assert result[0].resource_id == hosted_zone_id + assert result[0].resource_arn == hosted_zone_arn + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Route53 Hosted Zone {hosted_zone_id} is not protected by AWS Shield Advanced" + ) + + def test_shield_disabled_route53_hosted_zone_not_protected(self): + # Route53 Client + route53_client = mock.MagicMock + hosted_zone_id = "ABCDEF12345678" + hosted_zone_arn = f"arn:aws:route53:::hostedzone/{hosted_zone_id}" + hosted_zone_name = "test-hosted-zone" + + route53_client.hosted_zones = { + hosted_zone_id: HostedZone( + id=hosted_zone_id, + arn=hosted_zone_arn, + name=hosted_zone_name, + hosted_zone_name=hosted_zone_name, + private_zone=False, + region=AWS_REGION, + ) + } + + # Shield Client + shield_client = mock.MagicMock + shield_client.enabled = False + shield_client.region = AWS_REGION + shield_client.protections = {} + + with mock.patch( + "providers.aws.services.shield.shield_service.Shield", + new=shield_client, + ), mock.patch( + "providers.aws.services.route53.route53_service.Route53", + new=route53_client, + ): + # Test Check + from providers.aws.services.shield.shield_advanced_protection_in_route53_hosted_zones.shield_advanced_protection_in_route53_hosted_zones import ( + shield_advanced_protection_in_route53_hosted_zones, + ) + + check = shield_advanced_protection_in_route53_hosted_zones() + result = check.execute() + + assert len(result) == 0 diff --git a/providers/aws/services/shield/shield_client.py b/providers/aws/services/shield/shield_client.py new file mode 100644 index 00000000..c7ad41e5 --- /dev/null +++ b/providers/aws/services/shield/shield_client.py @@ -0,0 +1,4 @@ +from providers.aws.lib.audit_info.audit_info import current_audit_info +from providers.aws.services.shield.shield_service import Shield + +shield_client = Shield(current_audit_info) diff --git a/providers/aws/services/shield/shield_service.py b/providers/aws/services/shield/shield_service.py new file mode 100644 index 00000000..e8c5f58b --- /dev/null +++ b/providers/aws/services/shield/shield_service.py @@ -0,0 +1,64 @@ +from pydantic import BaseModel +from lib.logger import logger +from providers.aws.aws_provider import get_region_global_service + + +################### Shield +class Shield: + def __init__(self, audit_info): + self.service = "shield" + self.session = audit_info.audit_session + self.audited_account = audit_info.audited_account + self.client = self.session.client(self.service) + self.region = get_region_global_service(audit_info) + self.enabled = self.__get_subscription_state__() + self.protections = {} + self.__list_protections__() + + def __get_session__(self): + return self.session + + def __get_subscription_state__(self): + logger.info("Shield - Getting Subscription State...") + try: + return ( + True + if self.client.get_subscription_state()["SubscriptionState"] == "ACTIVE" + else False + ) + except Exception as error: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def __list_protections__(self): + logger.info("Shield - Listing Protections...") + try: + list_protections_paginator = self.client.get_paginator("list_protections") + for page in list_protections_paginator.paginate(): + for protection in page["Protections"]: + protection_arn = protection.get("ProtectionArn") + protection_id = protection.get("Id") + protection_name = protection.get("Name") + resource_arn = protection.get("ResourceArn") + + self.protections[protection_id] = Protection( + id=protection_id, + name=protection_name, + resource_arn=resource_arn, + protection_arn=protection_arn, + region=self.region, + ) + + except Exception as error: + logger.error( + f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + +class Protection(BaseModel): + id: str + name: str + resource_arn: str + protection_arn: str = None + region: str diff --git a/providers/aws/services/shield/shield_service_test.py b/providers/aws/services/shield/shield_service_test.py new file mode 100644 index 00000000..b9ac7c67 --- /dev/null +++ b/providers/aws/services/shield/shield_service_test.py @@ -0,0 +1,101 @@ +from providers.aws.lib.audit_info.models import AWS_Audit_Info +from providers.aws.services.shield.shield_service import Shield +from mock import patch +from moto.core import DEFAULT_ACCOUNT_ID +import botocore +from boto3 import session + +# Mock Test Region +AWS_REGION = "eu-west-1" + +# Mocking Access Analyzer Calls +make_api_call = botocore.client.BaseClient._make_api_call + + +def mock_make_api_call(self, operation_name, kwarg): + """We have to mock every AWS API call using Boto3""" + if operation_name == "ListProtections": + return { + "Protections": [ + { + "Id": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111", + "Name": "Protection for CloudFront distribution", + "ResourceArn": f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/E198WC25FXOWY8", + } + ] + } + if operation_name == "GetSubscriptionState": + return {"SubscriptionState": "ACTIVE"} + + return make_api_call(self, operation_name, kwarg) + + +# Patch every AWS call using Boto3 and generate_regional_clients to have 1 client +@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) +class Test_Shield_Service: + # 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=DEFAULT_ACCOUNT_ID, + audited_user_id=None, + audited_partition="aws", + audited_identity_arn=None, + profile=None, + profile_region=AWS_REGION, + credentials=None, + assumed_role_info=None, + audited_regions=None, + organizations_metadata=None, + ) + return audit_info + + # Test Shield Service + def test_service(self): + # Shield client for this test class + audit_info = self.set_mocked_audit_info() + shield = Shield(audit_info) + assert shield.service == "shield" + + # Test Shield Client + def test_client(self): + # Shield client for this test class + audit_info = self.set_mocked_audit_info() + shield = Shield(audit_info) + assert shield.client.__class__.__name__ == "Shield" + + # Test Shield Session + def test__get_session__(self): + # Shield client for this test class + audit_info = self.set_mocked_audit_info() + shield = Shield(audit_info) + assert shield.session.__class__.__name__ == "Session" + + def test__get_subscription_state__(self): + # Shield client for this test class + audit_info = self.set_mocked_audit_info() + shield = Shield(audit_info) + assert shield.enabled + + def test__list_protections__(self): + # Shield client for this test class + audit_info = self.set_mocked_audit_info() + shield = Shield(audit_info) + protection_id = "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111" + protection_name = "Protection for CloudFront distribution" + cloudfront_distribution_id = "E198WC25FXOWY8" + resource_arn = ( + f"arn:aws:cloudfront::{DEFAULT_ACCOUNT_ID}:distribution/{cloudfront_distribution_id}", + ) + + assert shield.protections + assert len(shield.protections) == 1 + assert shield.protections[protection_id] + assert shield.protections[protection_id].id == protection_id + assert shield.protections[protection_id].name == protection_name + assert not shield.protections[protection_id].protection_arn + assert not shield.protections[protection_id].resource_arn == resource_arn