mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-03-30 03:49:48 +00:00
Compare commits
6 Commits
feat/cisa-
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fea9633766 | ||
|
|
45f0909c3e | ||
|
|
b01fcc6cb2 | ||
|
|
2ddd5b3091 | ||
|
|
6100932c60 | ||
|
|
1c2b146e6e |
@@ -4,6 +4,10 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
|
||||
## [1.24.0] (Prowler UNRELEASED)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- Pin all unpinned dependencies to exact versions to prevent supply chain attacks and ensure reproducible builds [(#10469)](https://github.com/prowler-cloud/prowler/pull/10469)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Finding groups list/latest now apply computed status/severity filters and finding-level prefilters (delta, region, service, category, resource group, scan, resource type), plus `check_title` support for sort/filter consistency [(#10428)](https://github.com/prowler-cloud/prowler/pull/10428)
|
||||
|
||||
193
api/poetry.lock
generated
193
api/poetry.lock
generated
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "about-time"
|
||||
@@ -2469,22 +2469,18 @@ toml = ["tomli ; python_full_version <= \"3.11.0a6\""]
|
||||
|
||||
[[package]]
|
||||
name = "cron-descriptor"
|
||||
version = "2.0.6"
|
||||
version = "1.4.5"
|
||||
description = "A Python library that converts cron expressions into human readable strings."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "cron_descriptor-2.0.6-py3-none-any.whl", hash = "sha256:3a1c0d837c0e5a32e415f821b36cf758eb92d510e6beff8fbfe4fa16573d93d6"},
|
||||
{file = "cron_descriptor-2.0.6.tar.gz", hash = "sha256:e39d2848e1d8913cfb6e3452e701b5eec662ee18bea8cc5aa53ee1a7bb217157"},
|
||||
{file = "cron_descriptor-1.4.5-py3-none-any.whl", hash = "sha256:736b3ae9d1a99bc3dbfc5b55b5e6e7c12031e7ba5de716625772f8b02dcd6013"},
|
||||
{file = "cron_descriptor-1.4.5.tar.gz", hash = "sha256:f51ce4ffc1d1f2816939add8524f206c376a42c87a5fca3091ce26725b3b1bca"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typing_extensions = "*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["mypy", "polib", "ruff"]
|
||||
test = ["pytest"]
|
||||
dev = ["polib"]
|
||||
|
||||
[[package]]
|
||||
name = "crowdstrike-falconpy"
|
||||
@@ -2801,14 +2797,14 @@ bcrypt = ["bcrypt"]
|
||||
|
||||
[[package]]
|
||||
name = "django-allauth"
|
||||
version = "65.14.0"
|
||||
version = "65.15.0"
|
||||
description = "Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "django_allauth-65.14.0-py3-none-any.whl", hash = "sha256:448f5f7877f95fcbe1657256510fe7822d7871f202521a29e23ef937f3325a97"},
|
||||
{file = "django_allauth-65.14.0.tar.gz", hash = "sha256:5529227aba2b1377d900e9274a3f24496c645e65400fbae3cad5789944bc4d0b"},
|
||||
{file = "django_allauth-65.15.0-py3-none-any.whl", hash = "sha256:ad9fc49c49a9368eaa5bb95456b76e2a4f377b3c6862ee8443507816578c098d"},
|
||||
{file = "django_allauth-65.15.0.tar.gz", hash = "sha256:b404d48cf0c3ee14dacc834c541f30adedba2ff1c433980ecc494d6cb0b395a8"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -2831,20 +2827,20 @@ steam = ["python3-openid (>=3.0.8,<4)"]
|
||||
|
||||
[[package]]
|
||||
name = "django-celery-beat"
|
||||
version = "2.8.1"
|
||||
version = "2.9.0"
|
||||
description = "Database-backed Periodic Tasks."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "django_celery_beat-2.8.1-py3-none-any.whl", hash = "sha256:da2b1c6939495c05a551717509d6e3b79444e114a027f7b77bf3727c2a39d171"},
|
||||
{file = "django_celery_beat-2.8.1.tar.gz", hash = "sha256:dfad0201c0ac50c91a34700ef8fa0a10ee098cc7f3375fe5debed79f2204f80a"},
|
||||
{file = "django_celery_beat-2.9.0-py3-none-any.whl", hash = "sha256:4a9e5ebe26d6f8d7215e1fc5c46e466016279dc102435a28141108649bdf2157"},
|
||||
{file = "django_celery_beat-2.9.0.tar.gz", hash = "sha256:92404650f52fcb44cf08e2b09635cb1558327c54b1a5d570f0e2d3a22130934c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
celery = ">=5.2.3,<6.0"
|
||||
cron-descriptor = ">=1.2.32"
|
||||
Django = ">=2.2,<6.0"
|
||||
cron-descriptor = ">=1.2.32,<2.0.0"
|
||||
Django = ">=2.2,<6.1"
|
||||
django-timezone-field = ">=5.0"
|
||||
python-crontab = ">=2.3.4"
|
||||
tzdata = "*"
|
||||
@@ -3376,62 +3372,62 @@ dotenv = ["python-dotenv"]
|
||||
|
||||
[[package]]
|
||||
name = "fonttools"
|
||||
version = "4.61.1"
|
||||
version = "4.62.1"
|
||||
description = "Tools to manipulate font files"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24"},
|
||||
{file = "fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958"},
|
||||
{file = "fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da"},
|
||||
{file = "fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6"},
|
||||
{file = "fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1"},
|
||||
{file = "fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881"},
|
||||
{file = "fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47"},
|
||||
{file = "fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6"},
|
||||
{file = "fonttools-4.61.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c6604b735bb12fef8e0efd5578c9fb5d3d8532d5001ea13a19cddf295673ee09"},
|
||||
{file = "fonttools-4.61.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ce02f38a754f207f2f06557523cd39a06438ba3aafc0639c477ac409fc64e37"},
|
||||
{file = "fonttools-4.61.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77efb033d8d7ff233385f30c62c7c79271c8885d5c9657d967ede124671bbdfb"},
|
||||
{file = "fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:75c1a6dfac6abd407634420c93864a1e274ebc1c7531346d9254c0d8f6ca00f9"},
|
||||
{file = "fonttools-4.61.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0de30bfe7745c0d1ffa2b0b7048fb7123ad0d71107e10ee090fa0b16b9452e87"},
|
||||
{file = "fonttools-4.61.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58b0ee0ab5b1fc9921eccfe11d1435added19d6494dde14e323f25ad2bc30c56"},
|
||||
{file = "fonttools-4.61.1-cp311-cp311-win32.whl", hash = "sha256:f79b168428351d11e10c5aeb61a74e1851ec221081299f4cf56036a95431c43a"},
|
||||
{file = "fonttools-4.61.1-cp311-cp311-win_amd64.whl", hash = "sha256:fe2efccb324948a11dd09d22136fe2ac8a97d6c1347cf0b58a911dcd529f66b7"},
|
||||
{file = "fonttools-4.61.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f3cb4a569029b9f291f88aafc927dd53683757e640081ca8c412781ea144565e"},
|
||||
{file = "fonttools-4.61.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41a7170d042e8c0024703ed13b71893519a1a6d6e18e933e3ec7507a2c26a4b2"},
|
||||
{file = "fonttools-4.61.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10d88e55330e092940584774ee5e8a6971b01fc2f4d3466a1d6c158230880796"},
|
||||
{file = "fonttools-4.61.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15acc09befd16a0fb8a8f62bc147e1a82817542d72184acca9ce6e0aeda9fa6d"},
|
||||
{file = "fonttools-4.61.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6bcdf33aec38d16508ce61fd81838f24c83c90a1d1b8c68982857038673d6b8"},
|
||||
{file = "fonttools-4.61.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5fade934607a523614726119164ff621e8c30e8fa1ffffbbd358662056ba69f0"},
|
||||
{file = "fonttools-4.61.1-cp312-cp312-win32.whl", hash = "sha256:75da8f28eff26defba42c52986de97b22106cb8f26515b7c22443ebc9c2d3261"},
|
||||
{file = "fonttools-4.61.1-cp312-cp312-win_amd64.whl", hash = "sha256:497c31ce314219888c0e2fce5ad9178ca83fe5230b01a5006726cdf3ac9f24d9"},
|
||||
{file = "fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c"},
|
||||
{file = "fonttools-4.61.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e"},
|
||||
{file = "fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5"},
|
||||
{file = "fonttools-4.61.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c1b526c8d3f615a7b1867f38a9410849c8f4aef078535742198e942fba0e9bd"},
|
||||
{file = "fonttools-4.61.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41ed4b5ec103bd306bb68f81dc166e77409e5209443e5773cb4ed837bcc9b0d3"},
|
||||
{file = "fonttools-4.61.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b501c862d4901792adaec7c25b1ecc749e2662543f68bb194c42ba18d6eec98d"},
|
||||
{file = "fonttools-4.61.1-cp313-cp313-win32.whl", hash = "sha256:4d7092bb38c53bbc78e9255a59158b150bcdc115a1e3b3ce0b5f267dc35dd63c"},
|
||||
{file = "fonttools-4.61.1-cp313-cp313-win_amd64.whl", hash = "sha256:21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fff4f534200a04b4a36e7ae3cb74493afe807b517a09e99cb4faa89a34ed6ecd"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d9203500f7c63545b4ce3799319fe4d9feb1a1b89b28d3cb5abd11b9dd64147e"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa646ecec9528bef693415c79a86e733c70a4965dd938e9a226b0fc64c9d2e6c"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f35ad7805edba3aac1a3710d104592df59f4b957e30108ae0ba6c10b11dd75"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b931ae8f62db78861b0ff1ac017851764602288575d65b8e8ff1963fed419063"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b148b56f5de675ee16d45e769e69f87623a4944f7443850bf9a9376e628a89d2"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314-win32.whl", hash = "sha256:9b666a475a65f4e839d3d10473fad6d47e0a9db14a2f4a224029c5bfde58ad2c"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314-win_amd64.whl", hash = "sha256:4f5686e1fe5fce75d82d93c47a438a25bf0d1319d2843a926f741140b2b16e0c"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e76ce097e3c57c4bcb67c5aa24a0ecdbd9f74ea9219997a707a4061fbe2707aa"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9cfef3ab326780c04d6646f68d4b4742aae222e8b8ea1d627c74e38afcbc9d91"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75c301f96db737e1c5ed5fd7d77d9c34466de16095a266509e13da09751bd19"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91669ccac46bbc1d09e9273546181919064e8df73488ea087dcac3e2968df9ba"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c33ab3ca9d3ccd581d58e989d67554e42d8d4ded94ab3ade3508455fe70e65f7"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:664c5a68ec406f6b1547946683008576ef8b38275608e1cee6c061828171c118"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314t-win32.whl", hash = "sha256:aed04cabe26f30c1647ef0e8fbb207516fd40fe9472e9439695f5c6998e60ac5"},
|
||||
{file = "fonttools-4.61.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2180f14c141d2f0f3da43f3a81bc8aa4684860f6b0e6f9e165a4831f24e6a23b"},
|
||||
{file = "fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371"},
|
||||
{file = "fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69"},
|
||||
{file = "fonttools-4.62.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ad5cca75776cd453b1b035b530e943334957ae152a36a88a320e779d61fc980c"},
|
||||
{file = "fonttools-4.62.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b3ae47e8636156a9accff64c02c0924cbebad62854c4a6dbdc110cd5b4b341a"},
|
||||
{file = "fonttools-4.62.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b9e288b4da2f64fd6180644221749de651703e8d0c16bd4b719533a3a7d6e3"},
|
||||
{file = "fonttools-4.62.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7bca7a1c1faf235ffe25d4f2e555246b4750220b38de8261d94ebc5ce8a23c23"},
|
||||
{file = "fonttools-4.62.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4e0fcf265ad26e487c56cb12a42dffe7162de708762db951e1b3f755319507d"},
|
||||
{file = "fonttools-4.62.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2d850f66830a27b0d498ee05adb13a3781637b1826982cd7e2b3789ef0cc71ae"},
|
||||
{file = "fonttools-4.62.1-cp310-cp310-win32.whl", hash = "sha256:486f32c8047ccd05652aba17e4a8819a3a9d78570eb8a0e3b4503142947880ed"},
|
||||
{file = "fonttools-4.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:5a648bde915fba9da05ae98856987ca91ba832949a9e2888b48c47ef8b96c5a9"},
|
||||
{file = "fonttools-4.62.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:40975849bac44fb0b9253d77420c6d8b523ac4dcdcefeff6e4d706838a5b80f7"},
|
||||
{file = "fonttools-4.62.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9dde91633f77fa576879a0c76b1d89de373cae751a98ddf0109d54e173b40f14"},
|
||||
{file = "fonttools-4.62.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6acb4109f8bee00fec985c8c7afb02299e35e9c94b57287f3ea542f28bd0b0a7"},
|
||||
{file = "fonttools-4.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1c5c25671ce8805e0d080e2ffdeca7f1e86778c5cbfbeae86d7f866d8830517b"},
|
||||
{file = "fonttools-4.62.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a5d8825e1140f04e6c99bb7d37a9e31c172f3bc208afbe02175339e699c710e1"},
|
||||
{file = "fonttools-4.62.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:268abb1cb221e66c014acc234e872b7870d8b5d4657a83a8f4205094c32d2416"},
|
||||
{file = "fonttools-4.62.1-cp311-cp311-win32.whl", hash = "sha256:942b03094d7edbb99bdf1ae7e9090898cad7bf9030b3d21f33d7072dbcb51a53"},
|
||||
{file = "fonttools-4.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:e8514f4924375f77084e81467e63238b095abda5107620f49421c368a6017ed2"},
|
||||
{file = "fonttools-4.62.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974"},
|
||||
{file = "fonttools-4.62.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9"},
|
||||
{file = "fonttools-4.62.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936"},
|
||||
{file = "fonttools-4.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392"},
|
||||
{file = "fonttools-4.62.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04"},
|
||||
{file = "fonttools-4.62.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d"},
|
||||
{file = "fonttools-4.62.1-cp312-cp312-win32.whl", hash = "sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c"},
|
||||
{file = "fonttools-4.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42"},
|
||||
{file = "fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79"},
|
||||
{file = "fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe"},
|
||||
{file = "fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68"},
|
||||
{file = "fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1"},
|
||||
{file = "fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069"},
|
||||
{file = "fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9"},
|
||||
{file = "fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24"},
|
||||
{file = "fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a"},
|
||||
{file = "fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e"},
|
||||
{file = "fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd"},
|
||||
{file = "fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -5046,18 +5042,18 @@ tests = ["psutil", "pytest (!=3.3.0)", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "3.9"
|
||||
version = "3.10.2"
|
||||
description = "Python implementation of John Gruber's Markdown."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280"},
|
||||
{file = "markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a"},
|
||||
{file = "markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36"},
|
||||
{file = "markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"]
|
||||
docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python] (>=0.28.3)"]
|
||||
testing = ["coverage", "pyyaml"]
|
||||
|
||||
[[package]]
|
||||
@@ -6636,10 +6632,10 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "prowler"
|
||||
version = "5.22.0"
|
||||
version = "5.23.0"
|
||||
description = "Prowler is an Open Source security tool to perform AWS, GCP and Azure security best practices assessments, audits, incident response, continuous monitoring, hardening and forensics readiness. It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, FedRAMP, PCI-DSS, GDPR, HIPAA, FFIEC, SOC2, GXP, AWS Well-Architected Framework Security Pillar, AWS Foundational Technical Review (FTR), ENS (Spanish National Security Scheme) and your custom security frameworks."
|
||||
optional = false
|
||||
python-versions = ">3.9.1,<3.13"
|
||||
python-versions = ">=3.10,<3.13"
|
||||
groups = ["main"]
|
||||
files = []
|
||||
develop = false
|
||||
@@ -6702,7 +6698,7 @@ google-auth-httplib2 = ">=0.1,<0.3"
|
||||
h2 = "4.3.0"
|
||||
jsonschema = "4.23.0"
|
||||
kubernetes = "32.0.1"
|
||||
markdown = "3.9.0"
|
||||
markdown = "3.10.2"
|
||||
microsoft-kiota-abstractions = "1.9.2"
|
||||
msgraph-sdk = "1.23.0"
|
||||
numpy = "2.0.2"
|
||||
@@ -6726,7 +6722,7 @@ uuid6 = "2024.7.10"
|
||||
type = "git"
|
||||
url = "https://github.com/prowler-cloud/prowler.git"
|
||||
reference = "master"
|
||||
resolved_reference = "41629137efdec1ade078e4386f738c8e0ffce94b"
|
||||
resolved_reference = "2ddd5b3091bcdd8c7d44aba73b13c5c6f8f99e35"
|
||||
|
||||
[[package]]
|
||||
name = "psutil"
|
||||
@@ -6891,14 +6887,14 @@ pydantic = ">=2.12.0,<3.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "pyasn1"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pyasn1-0.6.2-py3-none-any.whl", hash = "sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf"},
|
||||
{file = "pyasn1-0.6.2.tar.gz", hash = "sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b"},
|
||||
{file = "pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde"},
|
||||
{file = "pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7349,14 +7345,14 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments
|
||||
|
||||
[[package]]
|
||||
name = "pytest-celery"
|
||||
version = "1.2.1"
|
||||
version = "1.3.0"
|
||||
description = "Pytest plugin for Celery"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8"
|
||||
python-versions = "<4.0,>=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pytest_celery-1.2.1-py3-none-any.whl", hash = "sha256:0441ab0c2a712b775be16ffda3d7deb31995fd7b5e9d71630e7ea98b474346a3"},
|
||||
{file = "pytest_celery-1.2.1.tar.gz", hash = "sha256:7873fb3cf4fbfe9b0dd15d359bdb8bbab4a41c7e48f5b0adb7d36138d3704d52"},
|
||||
{file = "pytest_celery-1.3.0-py3-none-any.whl", hash = "sha256:f02201d7770584a0c412a1ded329a142170c24012467c7046f2c72cc8205ad5d"},
|
||||
{file = "pytest_celery-1.3.0.tar.gz", hash = "sha256:bd9e5b0f594ec5de9ab97cf27e3a11c644718a761bab6b997d01800fd7394f64"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -7367,14 +7363,13 @@ kombu = "*"
|
||||
psutil = ">=7.0.0"
|
||||
pytest-docker-tools = ">=3.1.3"
|
||||
redis = {version = "*", optional = true, markers = "extra == \"all\" or extra == \"redis\""}
|
||||
setuptools = {version = ">=75.8.0", markers = "python_version >= \"3.9\" and python_version < \"4.0\""}
|
||||
tenacity = ">=9.0.0"
|
||||
|
||||
[package.extras]
|
||||
all = ["boto3", "botocore", "python-memcached", "redis", "urllib3 (>=1.26.16,<2.0)"]
|
||||
all = ["boto3", "botocore", "pycurl (>=7.43) ; sys_platform != \"win32\" and platform_python_implementation == \"CPython\"", "python-memcached", "redis", "urllib3 (>=1.26.16,<2.0)"]
|
||||
memcached = ["python-memcached"]
|
||||
redis = ["redis"]
|
||||
sqs = ["boto3", "botocore", "urllib3 (>=1.26.16,<2.0)"]
|
||||
sqs = ["boto3", "botocore", "pycurl (>=7.43) ; sys_platform != \"win32\" and platform_python_implementation == \"CPython\"", "urllib3 (>=1.26.16,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
@@ -7859,14 +7854,14 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "reportlab"
|
||||
version = "4.4.9"
|
||||
version = "4.4.10"
|
||||
description = "The Reportlab Toolkit"
|
||||
optional = false
|
||||
python-versions = "<4,>=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "reportlab-4.4.9-py3-none-any.whl", hash = "sha256:68e2d103ae8041a37714e8896ec9b79a1c1e911d68c3bd2ea17546568cf17bfd"},
|
||||
{file = "reportlab-4.4.9.tar.gz", hash = "sha256:7cf487764294ee791a4781f5a157bebce262a666ae4bbb87786760a9676c9378"},
|
||||
{file = "reportlab-4.4.10-py3-none-any.whl", hash = "sha256:5abc815746ae2bc44e7ff25db96814f921349ca814c992c7eac3c26029bf7c24"},
|
||||
{file = "reportlab-4.4.10.tar.gz", hash = "sha256:5cbbb34ac3546039d0086deb2938cdec06b12da3cdb836e813258eb33cd28487"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -8288,14 +8283,14 @@ contextlib2 = ">=0.5.5"
|
||||
|
||||
[[package]]
|
||||
name = "sentry-sdk"
|
||||
version = "2.51.0"
|
||||
version = "2.56.0"
|
||||
description = "Python client for Sentry (https://sentry.io)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "sentry_sdk-2.51.0-py2.py3-none-any.whl", hash = "sha256:e21016d318a097c2b617bb980afd9fc737e1efc55f9b4f0cdc819982c9717d5f"},
|
||||
{file = "sentry_sdk-2.51.0.tar.gz", hash = "sha256:b89d64577075fd8c13088bc3609a2ce77a154e5beb8cba7cc16560b0539df4f7"},
|
||||
{file = "sentry_sdk-2.56.0-py2.py3-none-any.whl", hash = "sha256:5afafb744ceb91d22f4cc650c6bd048ac6af5f7412dcc6c59305a2e36f4dbc02"},
|
||||
{file = "sentry_sdk-2.56.0.tar.gz", hash = "sha256:fdab72030b69625665b2eeb9738bdde748ad254e8073085a0ce95382678e8168"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -8768,14 +8763,14 @@ test = ["pytest", "websockets"]
|
||||
|
||||
[[package]]
|
||||
name = "werkzeug"
|
||||
version = "3.1.6"
|
||||
version = "3.1.7"
|
||||
description = "The comprehensive WSGI web application library."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "werkzeug-3.1.6-py3-none-any.whl", hash = "sha256:7ddf3357bb9564e407607f988f683d72038551200c704012bb9a4c523d42f131"},
|
||||
{file = "werkzeug-3.1.6.tar.gz", hash = "sha256:210c6bede5a420a913956b4791a7f4d6843a43b6fcee4dfa08a65e93007d0d25"},
|
||||
{file = "werkzeug-3.1.7-py3-none-any.whl", hash = "sha256:4b314d81163a3e1a169b6a0be2a000a0e204e8873c5de6586f453c55688d422f"},
|
||||
{file = "werkzeug-3.1.7.tar.gz", hash = "sha256:fb8c01fe6ab13b9b7cdb46892b99b1d66754e1d7ab8e542e865ec13f526b5351"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -9377,4 +9372,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.11,<3.13"
|
||||
content-hash = "2ed5b4e47d81da81963814f21702220ac5619f50cd605fd779be53c8c46ffca5"
|
||||
content-hash = "167d4549788b8bc8bb7772b9a81ade1eab73d8f354251a8d6af4901223cc7f67"
|
||||
|
||||
@@ -5,21 +5,21 @@ requires = ["poetry-core"]
|
||||
[project]
|
||||
authors = [{name = "Prowler Engineering", email = "engineering@prowler.com"}]
|
||||
dependencies = [
|
||||
"celery (>=5.4.0,<6.0.0)",
|
||||
"celery (==5.6.2)",
|
||||
"dj-rest-auth[with_social,jwt] (==7.0.1)",
|
||||
"django (==5.1.15)",
|
||||
"django-allauth[saml] (>=65.13.0,<66.0.0)",
|
||||
"django-celery-beat (>=2.7.0,<3.0.0)",
|
||||
"django-celery-results (>=2.5.1,<3.0.0)",
|
||||
"django-allauth[saml] (==65.15.0)",
|
||||
"django-celery-beat (==2.9.0)",
|
||||
"django-celery-results (==2.6.0)",
|
||||
"django-cors-headers==4.4.0",
|
||||
"django-environ==0.11.2",
|
||||
"django-filter==24.3",
|
||||
"django-guid==3.5.0",
|
||||
"django-postgres-extra (>=2.0.8,<3.0.0)",
|
||||
"django-postgres-extra (==2.0.9)",
|
||||
"djangorestframework==3.15.2",
|
||||
"djangorestframework-jsonapi==7.0.2",
|
||||
"djangorestframework-simplejwt (>=5.3.1,<6.0.0)",
|
||||
"drf-nested-routers (>=0.94.1,<1.0.0)",
|
||||
"djangorestframework-simplejwt (==5.5.1)",
|
||||
"drf-nested-routers (==0.95.0)",
|
||||
"drf-spectacular==0.27.2",
|
||||
"drf-spectacular-jsonapi==0.5.1",
|
||||
"defusedxml==0.7.1",
|
||||
@@ -27,22 +27,22 @@ dependencies = [
|
||||
"lxml==5.3.2",
|
||||
"prowler @ git+https://github.com/prowler-cloud/prowler.git@master",
|
||||
"psycopg2-binary==2.9.9",
|
||||
"pytest-celery[redis] (>=1.0.1,<2.0.0)",
|
||||
"sentry-sdk[django] (>=2.20.0,<3.0.0)",
|
||||
"pytest-celery[redis] (==1.3.0)",
|
||||
"sentry-sdk[django] (==2.56.0)",
|
||||
"uuid6==2024.7.10",
|
||||
"openai (>=1.82.0,<2.0.0)",
|
||||
"openai (==1.109.1)",
|
||||
"xmlsec==1.3.14",
|
||||
"h2 (==4.3.0)",
|
||||
"markdown (>=3.9,<4.0)",
|
||||
"markdown (==3.10.2)",
|
||||
"drf-simple-apikey (==2.2.1)",
|
||||
"matplotlib (>=3.10.6,<4.0.0)",
|
||||
"reportlab (>=4.4.4,<5.0.0)",
|
||||
"neo4j (>=6.0.0,<7.0.0)",
|
||||
"matplotlib (==3.10.8)",
|
||||
"reportlab (==4.4.10)",
|
||||
"neo4j (==6.1.0)",
|
||||
"cartography (==0.132.0)",
|
||||
"gevent (>=25.9.1,<26.0.0)",
|
||||
"werkzeug (>=3.1.4)",
|
||||
"sqlparse (>=0.5.4)",
|
||||
"fonttools (>=4.60.2)"
|
||||
"gevent (==25.9.1)",
|
||||
"werkzeug (==3.1.7)",
|
||||
"sqlparse (==0.5.5)",
|
||||
"fonttools (==4.62.1)"
|
||||
]
|
||||
description = "Prowler's API (Django/DRF)"
|
||||
license = "Apache-2.0"
|
||||
@@ -62,7 +62,7 @@ django-silk = "5.3.2"
|
||||
docker = "7.1.0"
|
||||
filelock = "3.20.3"
|
||||
freezegun = "1.5.1"
|
||||
marshmallow = ">=3.15.0,<4.0.0"
|
||||
marshmallow = "==3.26.2"
|
||||
mypy = "1.10.1"
|
||||
pylint = "3.2.5"
|
||||
pytest = "8.2.2"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 323 KiB After Width: | Height: | Size: 318 KiB |
199
poetry.lock
generated
199
poetry.lock
generated
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "about-time"
|
||||
@@ -808,7 +808,7 @@ description = "Timeout context manager for asyncio programs"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
markers = "python_version <= \"3.10\""
|
||||
markers = "python_version == \"3.10\""
|
||||
files = [
|
||||
{file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"},
|
||||
{file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"},
|
||||
@@ -1544,10 +1544,7 @@ files = [
|
||||
[package.dependencies]
|
||||
jmespath = ">=0.7.1,<2.0.0"
|
||||
python-dateutil = ">=2.1,<3.0.0"
|
||||
urllib3 = [
|
||||
{version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""},
|
||||
{version = ">=1.25.4,<1.27", markers = "python_version < \"3.10\""},
|
||||
]
|
||||
urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
crt = ["awscrt (==0.27.6)"]
|
||||
@@ -1827,22 +1824,6 @@ files = [
|
||||
{file = "circuitbreaker-2.1.3.tar.gz", hash = "sha256:1a4baee510f7bea3c91b194dcce7c07805fe96c4423ed5594b75af438531d084"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.8"
|
||||
description = "Composable command line interface toolkit"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version < \"3.10\""
|
||||
files = [
|
||||
{file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
|
||||
{file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.2.1"
|
||||
@@ -1850,7 +1831,6 @@ description = "Composable command line interface toolkit"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version >= \"3.10\""
|
||||
files = [
|
||||
{file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"},
|
||||
{file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"},
|
||||
@@ -1908,6 +1888,7 @@ files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
markers = {dev = "platform_system == \"Windows\" or sys_platform == \"win32\""}
|
||||
|
||||
[[package]]
|
||||
name = "contextlib2"
|
||||
@@ -2255,33 +2236,6 @@ docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"]
|
||||
ssh = ["paramiko (>=2.4.3)"]
|
||||
websockets = ["websocket-client (>=1.3.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "dogpile-cache"
|
||||
version = "1.4.1"
|
||||
description = "A caching front-end based on the Dogpile lock."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
markers = "python_version < \"3.10\""
|
||||
files = [
|
||||
{file = "dogpile_cache-1.4.1-py3-none-any.whl", hash = "sha256:99130ce990800c8d89c26a5a8d9923cbe1b78c8a9972c2aaa0abf3d2ef2984ad"},
|
||||
{file = "dogpile_cache-1.4.1.tar.gz", hash = "sha256:e25c60e677a5e28ff86124765fbf18c53257bcd7830749cd5ba350ace2a12989"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
decorator = ">=4.0.0"
|
||||
stevedore = ">=3.0.0"
|
||||
typing_extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
bmemcached = ["python-binary-memcached"]
|
||||
memcached = ["python-memcached"]
|
||||
pifpaf = ["pifpaf (>=3.2.0)"]
|
||||
pylibmc = ["pylibmc"]
|
||||
pymemcache = ["pymemcache"]
|
||||
redis = ["redis"]
|
||||
valkey = ["valkey"]
|
||||
|
||||
[[package]]
|
||||
name = "dogpile-cache"
|
||||
version = "1.5.0"
|
||||
@@ -2289,7 +2243,6 @@ description = "A caching front-end based on the Dogpile lock."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
markers = "python_version >= \"3.10\""
|
||||
files = [
|
||||
{file = "dogpile_cache-1.5.0-py3-none-any.whl", hash = "sha256:dc7b47d37844db15e8fdc0243c1b58857a2ddc52a5118237a97127bac200e18d"},
|
||||
{file = "dogpile_cache-1.5.0.tar.gz", hash = "sha256:849c5573c9a38f155cd4173103c702b637ede0361c12e864876877d0cd125eec"},
|
||||
@@ -2414,7 +2367,7 @@ description = "Backport of PEP 654 (exception groups)"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version <= \"3.10\""
|
||||
markers = "python_version == \"3.10\""
|
||||
files = [
|
||||
{file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
|
||||
{file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
|
||||
@@ -2441,19 +2394,6 @@ files = [
|
||||
[package.extras]
|
||||
testing = ["hatch", "pre-commit", "pytest", "tox"]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.19.1"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version < \"3.10\""
|
||||
files = [
|
||||
{file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"},
|
||||
{file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.20.3"
|
||||
@@ -2461,7 +2401,6 @@ description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version >= \"3.10\""
|
||||
files = [
|
||||
{file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"},
|
||||
{file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"},
|
||||
@@ -2499,7 +2438,6 @@ files = [
|
||||
[package.dependencies]
|
||||
blinker = ">=1.9.0"
|
||||
click = ">=8.1.3"
|
||||
importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""}
|
||||
itsdangerous = ">=2.2.0"
|
||||
jinja2 = ">=3.1.2"
|
||||
markupsafe = ">=2.1.1"
|
||||
@@ -2771,9 +2709,6 @@ files = [
|
||||
{file = "graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = {version = ">=4,<5", markers = "python_version < \"3.10\""}
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.16.0"
|
||||
@@ -2937,12 +2872,11 @@ version = "8.7.0"
|
||||
description = "Read metadata from Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main", "dev"]
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"},
|
||||
{file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"},
|
||||
]
|
||||
markers = {dev = "python_version < \"3.10\""}
|
||||
|
||||
[package.dependencies]
|
||||
zipp = ">=3.20"
|
||||
@@ -3137,7 +3071,7 @@ files = [
|
||||
|
||||
[package.dependencies]
|
||||
attrs = ">=22.2.0"
|
||||
jsonschema-specifications = ">=2023.03.6"
|
||||
jsonschema-specifications = ">=2023.3.6"
|
||||
referencing = ">=0.28.4"
|
||||
rpds-py = ">=0.7.1"
|
||||
|
||||
@@ -3178,34 +3112,6 @@ files = [
|
||||
[package.dependencies]
|
||||
referencing = ">=0.31.0"
|
||||
|
||||
[[package]]
|
||||
name = "keystoneauth1"
|
||||
version = "5.11.1"
|
||||
description = "Authentication Library for OpenStack Identity"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
markers = "python_version < \"3.10\""
|
||||
files = [
|
||||
{file = "keystoneauth1-5.11.1-py3-none-any.whl", hash = "sha256:4525adf03b6e591f4b9b8a72c3b14f6510a04816dd5a7aca6ebaa6dfc90b69e6"},
|
||||
{file = "keystoneauth1-5.11.1.tar.gz", hash = "sha256:806f12c49b7f4b2cad3f5a460f7bdd81e4247c81b6042596a7fea8575f6591f3"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
iso8601 = ">=2.0.0"
|
||||
os-service-types = ">=1.2.0"
|
||||
pbr = ">=2.0.0"
|
||||
requests = ">=2.14.2"
|
||||
stevedore = ">=1.20.0"
|
||||
typing-extensions = ">=4.12"
|
||||
|
||||
[package.extras]
|
||||
betamax = ["PyYAML (>=3.13)", "betamax (>=0.7.0)", "fixtures (>=3.0.0)"]
|
||||
kerberos = ["requests-kerberos (>=0.8.0)"]
|
||||
oauth1 = ["oauthlib (>=0.6.2)"]
|
||||
saml2 = ["lxml (>=4.2.0)"]
|
||||
test = ["PyYAML (>=3.12)", "bandit (>=1.7.6,<1.8.0)", "betamax (>=0.7.0)", "coverage (>=4.0)", "fixtures (>=3.0.0)", "flake8-docstrings (>=1.7.0,<1.8.0)", "flake8-import-order (>=0.18.2,<0.19.0)", "hacking (>=6.1.0,<6.2.0)", "lxml (>=4.2.0)", "oauthlib (>=0.6.2)", "oslo.config (>=5.2.0)", "oslo.utils (>=3.33.0)", "oslotest (>=3.2.0)", "requests-kerberos (>=0.8.0)", "requests-mock (>=1.2.0)", "stestr (>=1.0.0)", "testresources (>=2.0.0)", "testtools (>=2.2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "keystoneauth1"
|
||||
version = "5.13.0"
|
||||
@@ -3213,7 +3119,6 @@ description = "Authentication Library for OpenStack Identity"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
markers = "python_version >= \"3.10\""
|
||||
files = [
|
||||
{file = "keystoneauth1-5.13.0-py3-none-any.whl", hash = "sha256:5ab81412eb0923ceb9c602cc3decce514b399523cb83d16b409ed3b0f9b03d41"},
|
||||
{file = "keystoneauth1-5.13.0.tar.gz", hash = "sha256:57c9ca407207899b50d8ff1ca8abb4a4e7427461bfc1877eb8519c3989ce63ec"},
|
||||
@@ -3246,7 +3151,7 @@ files = [
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=14.05.14"
|
||||
certifi = ">=14.5.14"
|
||||
durationpy = ">=0.7"
|
||||
google-auth = ">=1.0.1"
|
||||
oauthlib = ">=3.2.2"
|
||||
@@ -3359,21 +3264,18 @@ tests = ["psutil", "pytest (!=3.3.0)", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "3.9"
|
||||
version = "3.10.2"
|
||||
description = "Python implementation of John Gruber's Markdown."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280"},
|
||||
{file = "markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a"},
|
||||
{file = "markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36"},
|
||||
{file = "markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"]
|
||||
docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python] (>=0.28.3)"]
|
||||
testing = ["coverage", "pyyaml"]
|
||||
|
||||
[[package]]
|
||||
@@ -4017,26 +3919,6 @@ files = [
|
||||
{file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "networkx"
|
||||
version = "3.2.1"
|
||||
description = "Python package for creating and manipulating graphs and networks"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
markers = "python_version < \"3.10\""
|
||||
files = [
|
||||
{file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"},
|
||||
{file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"]
|
||||
developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"]
|
||||
doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"]
|
||||
extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"]
|
||||
test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "networkx"
|
||||
version = "3.4.2"
|
||||
@@ -4324,22 +4206,6 @@ files = [
|
||||
opentelemetry-api = "1.35.0"
|
||||
typing-extensions = ">=4.5.0"
|
||||
|
||||
[[package]]
|
||||
name = "os-service-types"
|
||||
version = "1.7.0"
|
||||
description = "Python library for consuming OpenStack sevice-types-authority data"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
markers = "python_version < \"3.10\""
|
||||
files = [
|
||||
{file = "os-service-types-1.7.0.tar.gz", hash = "sha256:31800299a82239363995b91f1ebf9106ac7758542a1e4ef6dc737a5932878c6c"},
|
||||
{file = "os_service_types-1.7.0-py2.py3-none-any.whl", hash = "sha256:0505c72205690910077fb72b88f2a1f07533c8d39f2fe75b29583481764965d6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pbr = ">=2.0.0,<2.1.0 || >2.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "os-service-types"
|
||||
version = "1.8.2"
|
||||
@@ -4347,7 +4213,6 @@ description = "Python library for consuming OpenStack sevice-types-authority dat
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
markers = "python_version >= \"3.10\""
|
||||
files = [
|
||||
{file = "os_service_types-1.8.2-py3-none-any.whl", hash = "sha256:f78890d71814deffabf0ed4358288ec2ced579bc4d0bb87a79ae806cbb4deb6e"},
|
||||
{file = "os_service_types-1.8.2.tar.gz", hash = "sha256:ab7648d7232849943196e1bb00a30e2e25e600fa3b57bb241d15b7f521b5b575"},
|
||||
@@ -4856,7 +4721,7 @@ description = "C parser in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main", "dev"]
|
||||
markers = "implementation_name != \"PyPy\" and platform_python_implementation != \"PyPy\""
|
||||
markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\""
|
||||
files = [
|
||||
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
|
||||
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
|
||||
@@ -5098,7 +4963,7 @@ files = [
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
astroid = ">=3.3.8,<=3.4.0-dev0"
|
||||
astroid = ">=3.3.8,<=3.4.0.dev0"
|
||||
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
||||
dill = [
|
||||
{version = ">=0.2", markers = "python_version < \"3.11\""},
|
||||
@@ -5110,7 +4975,6 @@ mccabe = ">=0.6,<0.8"
|
||||
platformdirs = ">=2.2.0"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
tomlkit = ">=0.10.1"
|
||||
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
spelling = ["pyenchant (>=3.2,<4.0)"]
|
||||
@@ -5266,7 +5130,6 @@ files = [
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""}
|
||||
pytest = "*"
|
||||
|
||||
[[package]]
|
||||
@@ -5945,10 +5808,10 @@ files = [
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
botocore = ">=1.37.4,<2.0a.0"
|
||||
botocore = ">=1.37.4,<2.0a0"
|
||||
|
||||
[package.extras]
|
||||
crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"]
|
||||
crt = ["botocore[crt] (>=1.37.4,<2.0a0)"]
|
||||
|
||||
[[package]]
|
||||
name = "safety"
|
||||
@@ -6217,7 +6080,7 @@ description = "A lil' TOML parser"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
markers = "python_version <= \"3.10\""
|
||||
markers = "python_version == \"3.10\""
|
||||
files = [
|
||||
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
|
||||
@@ -6374,24 +6237,6 @@ files = [
|
||||
{file = "uritemplate-4.2.0.tar.gz", hash = "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.20"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version < \"3.10\""
|
||||
files = [
|
||||
{file = "urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e"},
|
||||
{file = "urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli (==1.0.9) ; os_name != \"nt\" and python_version < \"3\" and platform_python_implementation == \"CPython\"", "brotli (>=1.0.9) ; python_version >= \"3\" and platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; (os_name != \"nt\" or python_version >= \"3\") and platform_python_implementation != \"CPython\"", "brotlipy (>=0.6.0) ; os_name == \"nt\" and python_version < \"3\""]
|
||||
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress ; python_version == \"2.7\"", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.6.3"
|
||||
@@ -6399,7 +6244,6 @@ description = "HTTP library with thread-safe connection pooling, file post, and
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version >= \"3.10\""
|
||||
files = [
|
||||
{file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"},
|
||||
{file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"},
|
||||
@@ -6732,12 +6576,11 @@ version = "3.23.0"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main", "dev"]
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"},
|
||||
{file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"},
|
||||
]
|
||||
markers = {dev = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
|
||||
@@ -6885,5 +6728,5 @@ files = [
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">3.9.1,<3.13"
|
||||
content-hash = "fa67f98ae1b75ec5a54d1d6a1c33c5412d888ec60cf35fc407606dc48329c0bf"
|
||||
python-versions = ">=3.10,<3.13"
|
||||
content-hash = "65f1f9833d61f90f1f89ed70b3677f76c0693bae275dd39699df01c05050bbe6"
|
||||
|
||||
@@ -9,6 +9,11 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `apikeys_api_restricted_with_gemini_api` check for GCP provider [(#10280)](https://github.com/prowler-cloud/prowler/pull/10280)
|
||||
- `gemini_api_disabled` check for GCP provider [(#10280)](https://github.com/prowler-cloud/prowler/pull/10280)
|
||||
- `cloudfront_distributions_logging_enabled` detects Standard Logging v2 via CloudWatch Log Delivery [(#10090)](https://github.com/prowler-cloud/prowler/pull/10090)
|
||||
- `glue_etl_jobs_no_secrets_in_arguments` check for plaintext secrets in AWS Glue ETL job arguments [(#10368)](https://github.com/prowler-cloud/prowler/pull/10368)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
- Minimum Python version from 3.9 to 3.10 and updated classifiers to reflect supported versions (3.10, 3.11, 3.12) [(#10464)](https://github.com/prowler-cloud/prowler/pull/10464)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "glue_etl_jobs_no_secrets_in_arguments",
|
||||
"CheckTitle": "Glue ETL job has no secrets in default arguments",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"TTPs/Credential Access",
|
||||
"Effects/Data Exposure",
|
||||
"Sensitive Data Identifications/Security"
|
||||
],
|
||||
"ServiceName": "glue",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "critical",
|
||||
"ResourceType": "Other",
|
||||
"ResourceGroup": "analytics",
|
||||
"Description": "**AWS Glue ETL jobs** are inspected for **default arguments** (`DefaultArguments`) that resemble **secrets** (keys, tokens, passwords).\n\nSuch values indicate sensitive data is stored directly in job arguments instead of being sourced securely from AWS Secrets Manager or Systems Manager Parameter Store.",
|
||||
"Risk": "Plaintext secrets in default arguments reduce confidentiality: values can be viewed in consoles, CLI output, and CloudTrail logs. Compromised credentials enable unauthorized AWS actions, data exfiltration, and lateral movement across the environment.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.aws.amazon.com/glue/latest/dg/aws-glue-programming-etl-glue-arguments.html",
|
||||
"https://docs.aws.amazon.com/glue/latest/webapi/API_Job.html",
|
||||
"https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "aws glue update-job --job-name <job_name> --job-update '{\"DefaultArguments\":{\"--secret_name\":\"{{resolve:secretsmanager:my-secret}}\"}}'",
|
||||
"NativeIaC": "```yaml\nResources:\n GlueJob:\n Type: AWS::Glue::Job\n Properties:\n Name: <job_name>\n Role: <role_arn>\n Command:\n Name: glueetl\n ScriptLocation: \"s3://<bucket>/script.py\"\n DefaultArguments:\n \"--secret_name\": !Sub \"{{resolve:secretsmanager:${MySecret}}}\" # Reference secret from Secrets Manager instead of plaintext\n```",
|
||||
"Other": "1. Open the AWS Glue console and go to Jobs\n2. Select the job and click Edit\n3. Under Job parameters, identify any arguments containing sensitive values\n4. Store those values in AWS Secrets Manager or Systems Manager Parameter Store\n5. Update the job arguments to reference the secret by name or ARN instead of the plaintext value\n6. Save the job",
|
||||
"Terraform": "```hcl\nresource \"aws_glue_job\" \"example\" {\n name = \"<job_name>\"\n role_arn = \"<role_arn>\"\n\n command {\n script_location = \"s3://<bucket>/script.py\"\n }\n\n default_arguments = {\n \"--secret_name\" = aws_secretsmanager_secret_version.example.secret_string # Reference secret from Secrets Manager instead of plaintext\n }\n}\n```"
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Store secrets in **AWS Secrets Manager** or **AWS Systems Manager Parameter Store** and reference them by name or ARN in job arguments instead of embedding plaintext values. Enforce **least privilege** on the Glue job IAM role, rotate secrets regularly, and avoid logging or exporting argument values.",
|
||||
"Url": "https://hub.prowler.com/check/glue_etl_jobs_no_secrets_in_arguments"
|
||||
}
|
||||
},
|
||||
"Categories": [
|
||||
"secrets"
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import json
|
||||
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.lib.utils.utils import detect_secrets_scan
|
||||
from prowler.providers.aws.services.glue.glue_client import glue_client
|
||||
|
||||
|
||||
class glue_etl_jobs_no_secrets_in_arguments(Check):
|
||||
"""Check if Glue ETL jobs have secrets in their default arguments.
|
||||
|
||||
Scans the DefaultArguments of each Glue job for hardcoded credentials,
|
||||
tokens, passwords, and other sensitive values that should be stored in
|
||||
Secrets Manager or Parameter Store instead.
|
||||
"""
|
||||
|
||||
def execute(self):
|
||||
findings = []
|
||||
secrets_ignore_patterns = glue_client.audit_config.get(
|
||||
"secrets_ignore_patterns", []
|
||||
)
|
||||
for job in glue_client.jobs:
|
||||
report = Check_Report_AWS(metadata=self.metadata(), resource=job)
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"No secrets found in Glue job {job.name} default arguments."
|
||||
)
|
||||
|
||||
if job.arguments:
|
||||
secrets_found = []
|
||||
for arg_name, arg_value in job.arguments.items():
|
||||
detect_secrets_output = detect_secrets_scan(
|
||||
data=json.dumps({arg_name: arg_value}),
|
||||
excluded_secrets=secrets_ignore_patterns,
|
||||
detect_secrets_plugins=glue_client.audit_config.get(
|
||||
"detect_secrets_plugins",
|
||||
),
|
||||
)
|
||||
if detect_secrets_output:
|
||||
secrets_found.extend(
|
||||
[
|
||||
f"{secret['type']} in argument {arg_name}"
|
||||
for secret in detect_secrets_output
|
||||
]
|
||||
)
|
||||
|
||||
if secrets_found:
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Potential secrets found in Glue job {job.name} default arguments: {', '.join(secrets_found)}."
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -7,7 +7,9 @@ requires = ["poetry-core>=2.0"]
|
||||
authors = [{name = "Toni de la Fuente", email = "toni@blyx.com"}]
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"License :: OSI Approved :: Apache Software License"
|
||||
]
|
||||
dependencies = [
|
||||
@@ -54,7 +56,7 @@ dependencies = [
|
||||
"google-auth-httplib2>=0.1,<0.3",
|
||||
"jsonschema==4.23.0",
|
||||
"kubernetes==32.0.1",
|
||||
"markdown==3.9.0",
|
||||
"markdown==3.10.2",
|
||||
"microsoft-kiota-abstractions==1.9.2",
|
||||
"msgraph-sdk==1.23.0",
|
||||
"numpy==2.0.2",
|
||||
@@ -93,7 +95,7 @@ license = "Apache-2.0"
|
||||
maintainers = [{name = "Prowler Engineering", email = "engineering@prowler.com"}]
|
||||
name = "prowler"
|
||||
readme = "README.md"
|
||||
requires-python = ">3.9.1,<3.13"
|
||||
requires-python = ">=3.10,<3.13"
|
||||
version = "5.23.0"
|
||||
|
||||
[project.scripts]
|
||||
@@ -117,13 +119,10 @@ bandit = "1.8.3"
|
||||
black = "25.1.0"
|
||||
coverage = "7.6.12"
|
||||
docker = "7.1.0"
|
||||
filelock = [
|
||||
{version = "3.20.3", python = ">=3.10"},
|
||||
{version = "3.19.1", python = "<3.10"}
|
||||
]
|
||||
filelock = "3.20.3"
|
||||
flake8 = "7.1.2"
|
||||
freezegun = "1.5.1"
|
||||
marshmallow = ">=3.15.0,<4.0.0"
|
||||
marshmallow = "==3.26.2"
|
||||
mock = "5.2.0"
|
||||
moto = {extras = ["all"], version = "5.1.11"}
|
||||
openapi-schema-validator = "0.6.3"
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
from unittest import mock
|
||||
|
||||
from boto3 import client
|
||||
from moto import mock_aws
|
||||
|
||||
from tests.providers.aws.utils import (
|
||||
AWS_ACCOUNT_NUMBER,
|
||||
AWS_REGION_US_EAST_1,
|
||||
set_mocked_aws_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_glue_etl_jobs_no_secrets_in_arguments:
|
||||
@mock_aws
|
||||
def test_glue_no_jobs(self):
|
||||
from prowler.providers.aws.services.glue.glue_service import Glue
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.glue.glue_etl_jobs_no_secrets_in_arguments.glue_etl_jobs_no_secrets_in_arguments.glue_client",
|
||||
new=Glue(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.glue.glue_etl_jobs_no_secrets_in_arguments.glue_etl_jobs_no_secrets_in_arguments import (
|
||||
glue_etl_jobs_no_secrets_in_arguments,
|
||||
)
|
||||
|
||||
check = glue_etl_jobs_no_secrets_in_arguments()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
@mock_aws
|
||||
def test_glue_job_no_secrets(self):
|
||||
glue_client = client("glue", region_name=AWS_REGION_US_EAST_1)
|
||||
job_name = "test-job"
|
||||
job_arn = (
|
||||
f"arn:aws:glue:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:job/{job_name}"
|
||||
)
|
||||
glue_client.create_job(
|
||||
Name=job_name,
|
||||
Role="role_test",
|
||||
Command={"Name": "name_test", "ScriptLocation": "script_test"},
|
||||
DefaultArguments={
|
||||
"--enable-continuous-cloudwatch-log": "true",
|
||||
"--TempDir": "s3://my-bucket/temp/",
|
||||
},
|
||||
Tags={"key_test": "value_test"},
|
||||
GlueVersion="1.0",
|
||||
MaxCapacity=0.0625,
|
||||
MaxRetries=0,
|
||||
Timeout=10,
|
||||
NumberOfWorkers=2,
|
||||
WorkerType="G.1X",
|
||||
)
|
||||
|
||||
from prowler.providers.aws.services.glue.glue_service import Glue
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.glue.glue_etl_jobs_no_secrets_in_arguments.glue_etl_jobs_no_secrets_in_arguments.glue_client",
|
||||
new=Glue(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.glue.glue_etl_jobs_no_secrets_in_arguments.glue_etl_jobs_no_secrets_in_arguments import (
|
||||
glue_etl_jobs_no_secrets_in_arguments,
|
||||
)
|
||||
|
||||
check = glue_etl_jobs_no_secrets_in_arguments()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"No secrets found in Glue job {job_name} default arguments."
|
||||
)
|
||||
assert result[0].resource_id == job_name
|
||||
assert result[0].resource_arn == job_arn
|
||||
assert result[0].resource_tags == [{"key_test": "value_test"}]
|
||||
|
||||
@mock_aws
|
||||
def test_glue_job_with_secrets(self):
|
||||
glue_client = client("glue", region_name=AWS_REGION_US_EAST_1)
|
||||
job_name = "test-job"
|
||||
job_arn = (
|
||||
f"arn:aws:glue:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:job/{job_name}"
|
||||
)
|
||||
glue_client.create_job(
|
||||
Name=job_name,
|
||||
Role="role_test",
|
||||
Command={"Name": "name_test", "ScriptLocation": "script_test"},
|
||||
DefaultArguments={
|
||||
"--db-password": "AKIAsupersecretkey1234",
|
||||
"--TempDir": "s3://my-bucket/temp/",
|
||||
},
|
||||
Tags={"key_test": "value_test"},
|
||||
GlueVersion="1.0",
|
||||
MaxCapacity=0.0625,
|
||||
MaxRetries=0,
|
||||
Timeout=10,
|
||||
NumberOfWorkers=2,
|
||||
WorkerType="G.1X",
|
||||
)
|
||||
|
||||
from prowler.providers.aws.services.glue.glue_service import Glue
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.glue.glue_etl_jobs_no_secrets_in_arguments.glue_etl_jobs_no_secrets_in_arguments.glue_client",
|
||||
new=Glue(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.glue.glue_etl_jobs_no_secrets_in_arguments.glue_etl_jobs_no_secrets_in_arguments import (
|
||||
glue_etl_jobs_no_secrets_in_arguments,
|
||||
)
|
||||
|
||||
check = glue_etl_jobs_no_secrets_in_arguments()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert "Potential secrets found" in result[0].status_extended
|
||||
assert job_name in result[0].status_extended
|
||||
assert "--db-password" in result[0].status_extended
|
||||
assert result[0].resource_id == job_name
|
||||
assert result[0].resource_arn == job_arn
|
||||
assert result[0].resource_tags == [{"key_test": "value_test"}]
|
||||
|
||||
@mock_aws
|
||||
def test_glue_job_empty_arguments(self):
|
||||
glue_client = client("glue", region_name=AWS_REGION_US_EAST_1)
|
||||
job_name = "test-job"
|
||||
job_arn = (
|
||||
f"arn:aws:glue:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:job/{job_name}"
|
||||
)
|
||||
glue_client.create_job(
|
||||
Name=job_name,
|
||||
Role="role_test",
|
||||
Command={"Name": "name_test", "ScriptLocation": "script_test"},
|
||||
DefaultArguments={},
|
||||
Tags={"key_test": "value_test"},
|
||||
GlueVersion="1.0",
|
||||
MaxCapacity=0.0625,
|
||||
MaxRetries=0,
|
||||
Timeout=10,
|
||||
NumberOfWorkers=2,
|
||||
WorkerType="G.1X",
|
||||
)
|
||||
|
||||
from prowler.providers.aws.services.glue.glue_service import Glue
|
||||
|
||||
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=aws_provider,
|
||||
):
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.glue.glue_etl_jobs_no_secrets_in_arguments.glue_etl_jobs_no_secrets_in_arguments.glue_client",
|
||||
new=Glue(aws_provider),
|
||||
):
|
||||
from prowler.providers.aws.services.glue.glue_etl_jobs_no_secrets_in_arguments.glue_etl_jobs_no_secrets_in_arguments import (
|
||||
glue_etl_jobs_no_secrets_in_arguments,
|
||||
)
|
||||
|
||||
check = glue_etl_jobs_no_secrets_in_arguments()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"No secrets found in Glue job {job_name} default arguments."
|
||||
)
|
||||
assert result[0].resource_id == job_name
|
||||
assert result[0].resource_arn == job_arn
|
||||
assert result[0].resource_tags == [{"key_test": "value_test"}]
|
||||
@@ -2,6 +2,14 @@
|
||||
|
||||
All notable changes to the **Prowler UI** are documented in this file.
|
||||
|
||||
## [1.23.0] (Prowler UNRELEASED)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Clear Filters now resets all filters including muted findings and auto-applies, Clear all in pills only removes pill-visible sub-filters, and the discard icon is now an Undo text button [(#10446)](https://github.com/prowler-cloud/prowler/pull/10446)
|
||||
|
||||
---
|
||||
|
||||
## [1.22.0] (Prowler v5.22.0)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
@@ -5,7 +5,6 @@ import { describe, expect, it, vi } from "vitest";
|
||||
// Mock lucide-react to avoid SVG rendering issues in jsdom
|
||||
vi.mock("lucide-react", () => ({
|
||||
Check: () => <svg data-testid="check-icon" />,
|
||||
X: () => <svg data-testid="x-icon" />,
|
||||
}));
|
||||
|
||||
// Mock @/components/shadcn to avoid next-auth import chain
|
||||
@@ -74,7 +73,7 @@ describe("ApplyFiltersButton", () => {
|
||||
expect(applyButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should NOT render the discard (X) button when there are no changes", () => {
|
||||
it("should NOT render the Undo button when there are no changes", () => {
|
||||
// Given / When
|
||||
render(
|
||||
<ApplyFiltersButton
|
||||
@@ -88,7 +87,7 @@ describe("ApplyFiltersButton", () => {
|
||||
// Then
|
||||
expect(
|
||||
screen.queryByRole("button", {
|
||||
name: /discard/i,
|
||||
name: /undo/i,
|
||||
}),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
@@ -166,7 +165,7 @@ describe("ApplyFiltersButton", () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render the discard (X) button", () => {
|
||||
it("should render the Undo button", () => {
|
||||
// Given / When
|
||||
render(
|
||||
<ApplyFiltersButton
|
||||
@@ -179,7 +178,7 @@ describe("ApplyFiltersButton", () => {
|
||||
|
||||
// Then
|
||||
expect(
|
||||
screen.getByRole("button", { name: /discard pending filter changes/i }),
|
||||
screen.getByRole("button", { name: /undo pending filter changes/i }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -237,7 +236,7 @@ describe("ApplyFiltersButton", () => {
|
||||
// ── onDiscard interaction ────────────────────────────────────────────────
|
||||
|
||||
describe("onDiscard", () => {
|
||||
it("should call onDiscard when the Discard button is clicked", async () => {
|
||||
it("should call onDiscard when the Undo button is clicked", async () => {
|
||||
// Given
|
||||
const user = userEvent.setup();
|
||||
const onApply = vi.fn();
|
||||
@@ -254,7 +253,7 @@ describe("ApplyFiltersButton", () => {
|
||||
|
||||
// When
|
||||
await user.click(
|
||||
screen.getByRole("button", { name: /discard pending filter changes/i }),
|
||||
screen.getByRole("button", { name: /undo pending filter changes/i }),
|
||||
);
|
||||
|
||||
// Then
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Check, X } from "lucide-react";
|
||||
import { Check } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/shadcn";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -12,7 +12,7 @@ export interface ApplyFiltersButtonProps {
|
||||
changeCount: number;
|
||||
/** Called when the user clicks "Apply Filters" */
|
||||
onApply: () => void;
|
||||
/** Called when the user clicks the discard (X) action */
|
||||
/** Called when the user clicks the discard (Undo) action */
|
||||
onDiscard: () => void;
|
||||
/** Optional extra class names for the outer wrapper */
|
||||
className?: string;
|
||||
@@ -23,7 +23,7 @@ export interface ApplyFiltersButtonProps {
|
||||
*
|
||||
* - Shows the count of pending changes when `hasChanges` is true.
|
||||
* - The apply button is disabled (and visually muted) when there are no changes.
|
||||
* - The discard (X) button only appears when there are pending changes.
|
||||
* - The Undo button only appears when there are pending changes.
|
||||
* - Uses Prowler's shadcn `Button` component.
|
||||
*/
|
||||
export const ApplyFiltersButton = ({
|
||||
@@ -52,11 +52,11 @@ export const ApplyFiltersButton = ({
|
||||
{hasChanges && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
size="sm"
|
||||
onClick={onDiscard}
|
||||
aria-label="Discard pending filter changes"
|
||||
aria-label="Undo pending filter changes"
|
||||
>
|
||||
<X className="size-4" />
|
||||
Undo
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { XCircle } from "lucide-react";
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { Button } from "../shadcn";
|
||||
|
||||
@@ -52,7 +51,7 @@ export const ClearFiltersButton = ({
|
||||
const filterCount = activeFilters.length;
|
||||
|
||||
// Clear all filters except excluded ones (muted, search)
|
||||
const clearFiltersPreservingExcluded = useCallback(() => {
|
||||
const clearFiltersPreservingExcluded = () => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
Array.from(params.keys()).forEach((key) => {
|
||||
if (
|
||||
@@ -64,7 +63,7 @@ export const ClearFiltersButton = ({
|
||||
});
|
||||
params.delete("page");
|
||||
router.push(`${pathname}?${params.toString()}`, { scroll: false });
|
||||
}, [router, searchParams, pathname]);
|
||||
};
|
||||
|
||||
// In batch mode: use pendingCount if provided; otherwise fall back to URL count.
|
||||
// In instant mode: always use URL count.
|
||||
|
||||
@@ -118,7 +118,8 @@ export const FindingsFilters = ({
|
||||
setPending,
|
||||
applyAll,
|
||||
discardAll,
|
||||
clearAll,
|
||||
clearAndApply,
|
||||
clearKeys,
|
||||
hasChanges,
|
||||
changeCount,
|
||||
getFilterValue,
|
||||
@@ -198,6 +199,20 @@ export const FindingsFilters = ({
|
||||
setPending(filterKey, nextValues);
|
||||
};
|
||||
|
||||
// Handler for clearing all chips: clears only the filter keys visible as chips,
|
||||
// without touching provider/account selectors.
|
||||
const PROVIDER_KEYS = new Set([
|
||||
"filter[provider_type__in]",
|
||||
"filter[provider_id__in]",
|
||||
]);
|
||||
|
||||
const handleClearAllChips = () => {
|
||||
const chipKeys = Array.from(new Set(filterChips.map((c) => c.key))).filter(
|
||||
(k) => !PROVIDER_KEYS.has(k),
|
||||
);
|
||||
clearKeys(chipKeys);
|
||||
};
|
||||
|
||||
// Derive pending muted state for the checkbox.
|
||||
// Note: "filter[muted]" participates in batch mode — applyAll includes it
|
||||
// when present in pending state, and the defaultParams option ensures
|
||||
@@ -252,7 +267,7 @@ export const FindingsFilters = ({
|
||||
)}
|
||||
<ClearFiltersButton
|
||||
showCount
|
||||
onClear={clearAll}
|
||||
onClear={clearAndApply}
|
||||
pendingCount={
|
||||
Object.entries(pendingFilters).filter(([key, values]) => {
|
||||
if (!values || values.length === 0) return false;
|
||||
@@ -279,7 +294,7 @@ export const FindingsFilters = ({
|
||||
<FilterSummaryStrip
|
||||
chips={filterChips}
|
||||
onRemove={handleChipRemove}
|
||||
onClearAll={clearAll}
|
||||
onClearAll={handleClearAllChips}
|
||||
/>
|
||||
|
||||
{/* Expandable filters section */}
|
||||
|
||||
@@ -585,4 +585,264 @@ describe("useFilterBatch", () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// ── clearAndApply ──────────────────────────────────────────────────────────
|
||||
|
||||
describe("clearAndApply", () => {
|
||||
it("should clear all batch-managed filters and push URL immediately", () => {
|
||||
// Given
|
||||
setSearchParams({
|
||||
"filter[severity__in]": "critical",
|
||||
"filter[status__in]": "FAIL",
|
||||
});
|
||||
const { result } = renderHook(() => useFilterBatch());
|
||||
|
||||
// Pre-condition — pending is loaded from URL
|
||||
expect(result.current.pendingFilters["filter[severity__in]"]).toEqual([
|
||||
"critical",
|
||||
]);
|
||||
expect(result.current.pendingFilters["filter[status__in]"]).toEqual([
|
||||
"FAIL",
|
||||
]);
|
||||
|
||||
// When
|
||||
act(() => {
|
||||
result.current.clearAndApply();
|
||||
});
|
||||
|
||||
// Then — pending is empty
|
||||
expect(result.current.pendingFilters).toEqual({});
|
||||
|
||||
// And router.push was called
|
||||
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||
const calledUrl: string = mockPush.mock.calls[0][0];
|
||||
|
||||
// The pushed URL must NOT contain severity or status
|
||||
expect(calledUrl).not.toContain("severity");
|
||||
expect(calledUrl).not.toContain("status");
|
||||
});
|
||||
|
||||
it("should apply defaultParams when clearing", () => {
|
||||
// Given
|
||||
setSearchParams({ "filter[severity__in]": "critical" });
|
||||
const { result } = renderHook(() =>
|
||||
useFilterBatch({ defaultParams: { "filter[muted]": "false" } }),
|
||||
);
|
||||
|
||||
// When
|
||||
act(() => {
|
||||
result.current.clearAndApply();
|
||||
});
|
||||
|
||||
// Then — pushed URL contains the defaultParam
|
||||
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||
const calledUrl: string = mockPush.mock.calls[0][0];
|
||||
expect(calledUrl).toContain("filter%5Bmuted%5D=false");
|
||||
});
|
||||
|
||||
it("should preserve filter[search] (excluded from batch)", () => {
|
||||
// Given — URL has both a search param (excluded) and a batch filter
|
||||
mockSearchParamsValue = new URLSearchParams({
|
||||
"filter[search]": "test",
|
||||
"filter[severity__in]": "critical",
|
||||
});
|
||||
const { result } = renderHook(() => useFilterBatch());
|
||||
|
||||
// When
|
||||
act(() => {
|
||||
result.current.clearAndApply();
|
||||
});
|
||||
|
||||
// Then — search param is preserved; severity is gone
|
||||
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||
const calledUrl: string = mockPush.mock.calls[0][0];
|
||||
expect(calledUrl).toContain("filter%5Bsearch%5D=test");
|
||||
expect(calledUrl).not.toContain("severity");
|
||||
});
|
||||
|
||||
it("should reset pagination to page 1", () => {
|
||||
// Given — URL already has a page param
|
||||
mockSearchParamsValue = new URLSearchParams({
|
||||
"filter[severity__in]": "critical",
|
||||
page: "3",
|
||||
});
|
||||
const { result } = renderHook(() => useFilterBatch());
|
||||
|
||||
// When
|
||||
act(() => {
|
||||
result.current.clearAndApply();
|
||||
});
|
||||
|
||||
// Then — page is reset to 1
|
||||
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||
const calledUrl: string = mockPush.mock.calls[0][0];
|
||||
expect(calledUrl).toContain("page=1");
|
||||
});
|
||||
});
|
||||
|
||||
// ── clearKeys ─────────────────────────────────────────────────────────────
|
||||
|
||||
describe("clearKeys", () => {
|
||||
it("should remove only specified keys and push URL", () => {
|
||||
// Given
|
||||
setSearchParams({});
|
||||
const { result } = renderHook(() => useFilterBatch());
|
||||
|
||||
act(() => {
|
||||
result.current.setPending("filter[severity__in]", ["critical"]);
|
||||
result.current.setPending("filter[status__in]", ["FAIL"]);
|
||||
result.current.setPending("filter[region__in]", ["us-east-1"]);
|
||||
});
|
||||
|
||||
// When
|
||||
act(() => {
|
||||
result.current.clearKeys(["filter[severity__in]"]);
|
||||
});
|
||||
|
||||
// Then — severity is gone; status and region remain
|
||||
expect(
|
||||
result.current.pendingFilters["filter[severity__in]"],
|
||||
).toBeUndefined();
|
||||
expect(result.current.pendingFilters["filter[status__in]"]).toEqual([
|
||||
"FAIL",
|
||||
]);
|
||||
expect(result.current.pendingFilters["filter[region__in]"]).toEqual([
|
||||
"us-east-1",
|
||||
]);
|
||||
|
||||
// And the pushed URL contains the remaining keys but not severity
|
||||
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||
const calledUrl: string = mockPush.mock.calls[0][0];
|
||||
expect(calledUrl).toContain("status");
|
||||
expect(calledUrl).toContain("region");
|
||||
expect(calledUrl).not.toContain("severity");
|
||||
});
|
||||
|
||||
it("should accept keys without 'filter[' prefix", () => {
|
||||
// Given
|
||||
setSearchParams({});
|
||||
const { result } = renderHook(() => useFilterBatch());
|
||||
|
||||
act(() => {
|
||||
result.current.setPending("filter[severity__in]", ["critical"]);
|
||||
});
|
||||
|
||||
// When — pass key without filter[] wrapper
|
||||
act(() => {
|
||||
result.current.clearKeys(["severity__in"]);
|
||||
});
|
||||
|
||||
// Then — severity is cleared
|
||||
expect(
|
||||
result.current.pendingFilters["filter[severity__in]"],
|
||||
).toBeUndefined();
|
||||
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||
const calledUrl: string = mockPush.mock.calls[0][0];
|
||||
expect(calledUrl).not.toContain("severity");
|
||||
});
|
||||
|
||||
it("should preserve provider/account keys not in the cleared list", () => {
|
||||
// Given
|
||||
setSearchParams({});
|
||||
const { result } = renderHook(() => useFilterBatch());
|
||||
|
||||
act(() => {
|
||||
result.current.setPending("filter[provider_type__in]", ["aws"]);
|
||||
result.current.setPending("filter[severity__in]", ["critical"]);
|
||||
result.current.setPending("filter[status__in]", ["FAIL"]);
|
||||
});
|
||||
|
||||
// When — clear only severity and status; leave provider untouched
|
||||
act(() => {
|
||||
result.current.clearKeys([
|
||||
"filter[severity__in]",
|
||||
"filter[status__in]",
|
||||
]);
|
||||
});
|
||||
|
||||
// Then — provider_type__in is still in pending
|
||||
expect(
|
||||
result.current.pendingFilters["filter[provider_type__in]"],
|
||||
).toEqual(["aws"]);
|
||||
expect(
|
||||
result.current.pendingFilters["filter[severity__in]"],
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
result.current.pendingFilters["filter[status__in]"],
|
||||
).toBeUndefined();
|
||||
|
||||
// And the pushed URL retains provider but not severity/status
|
||||
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||
const calledUrl: string = mockPush.mock.calls[0][0];
|
||||
expect(calledUrl).toContain("provider_type__in");
|
||||
expect(calledUrl).not.toContain("severity");
|
||||
expect(calledUrl).not.toContain("status__in");
|
||||
});
|
||||
|
||||
it("should apply defaultParams after clearing", () => {
|
||||
// Given
|
||||
setSearchParams({});
|
||||
const { result } = renderHook(() =>
|
||||
useFilterBatch({ defaultParams: { "filter[muted]": "false" } }),
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.setPending("filter[severity__in]", ["critical"]);
|
||||
});
|
||||
|
||||
// When
|
||||
act(() => {
|
||||
result.current.clearKeys(["filter[severity__in]"]);
|
||||
});
|
||||
|
||||
// Then — defaultParam is present in the pushed URL
|
||||
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||
const calledUrl: string = mockPush.mock.calls[0][0];
|
||||
expect(calledUrl).toContain("filter%5Bmuted%5D=false");
|
||||
});
|
||||
|
||||
it("should reset pagination to page 1", () => {
|
||||
// Given — URL already has a page param
|
||||
mockSearchParamsValue = new URLSearchParams({
|
||||
"filter[severity__in]": "critical",
|
||||
page: "5",
|
||||
});
|
||||
const { result } = renderHook(() => useFilterBatch());
|
||||
|
||||
// When
|
||||
act(() => {
|
||||
result.current.clearKeys(["filter[severity__in]"]);
|
||||
});
|
||||
|
||||
// Then — page is reset to 1
|
||||
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||
const calledUrl: string = mockPush.mock.calls[0][0];
|
||||
expect(calledUrl).toContain("page=1");
|
||||
});
|
||||
|
||||
it("should handle empty keys array gracefully", () => {
|
||||
// Given
|
||||
setSearchParams({});
|
||||
const { result } = renderHook(() => useFilterBatch());
|
||||
|
||||
act(() => {
|
||||
result.current.setPending("filter[severity__in]", ["critical"]);
|
||||
});
|
||||
|
||||
// When — clear no keys at all
|
||||
act(() => {
|
||||
result.current.clearKeys([]);
|
||||
});
|
||||
|
||||
// Then — pending is unchanged
|
||||
expect(result.current.pendingFilters["filter[severity__in]"]).toEqual([
|
||||
"critical",
|
||||
]);
|
||||
|
||||
// And router.push was still called (navigates with current state)
|
||||
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||
const calledUrl: string = mockPush.mock.calls[0][0];
|
||||
expect(calledUrl).toContain("severity");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,6 +32,19 @@ export interface UseFilterBatchReturn {
|
||||
* Includes provider/account keys and all batch-managed filter keys.
|
||||
*/
|
||||
clearAll: () => void;
|
||||
/**
|
||||
* Clear all batch-managed filters and immediately navigate (router.push)
|
||||
* with defaultParams applied. Equivalent to clearAll() + applyAll() but
|
||||
* avoids the async state gap between the two calls.
|
||||
*/
|
||||
clearAndApply: () => void;
|
||||
/**
|
||||
* Clear only the specified filter keys from pending state and immediately
|
||||
* navigate (router.push) with the remaining pending filters + defaultParams.
|
||||
* Used by "Clear all" in the pills strip to remove only pill-visible filters
|
||||
* without touching provider/account selectors.
|
||||
*/
|
||||
clearKeys: (keys: string[]) => void;
|
||||
/** Remove a single filter key from pending state */
|
||||
removePending: (key: string) => void;
|
||||
/** Whether pending state differs from the current URL */
|
||||
@@ -164,20 +177,19 @@ export const useFilterBatch = (
|
||||
});
|
||||
};
|
||||
|
||||
const applyAll = () => {
|
||||
// Start from the current URL params to preserve non-batch params.
|
||||
// Only filter[search] is excluded from batch management and preserved from the URL as-is.
|
||||
/** Private helper — builds URLSearchParams from a pending state and pushes. */
|
||||
const buildAndPush = (nextPending: PendingFilters) => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
|
||||
// Remove all existing batch-managed filter params
|
||||
// Remove all batch-managed filter params
|
||||
Array.from(params.keys()).forEach((key) => {
|
||||
if (key.startsWith("filter[") && !EXCLUDED_FROM_BATCH.includes(key)) {
|
||||
params.delete(key);
|
||||
}
|
||||
});
|
||||
|
||||
// Write the pending state
|
||||
Object.entries(pendingFilters).forEach(([key, values]) => {
|
||||
// Re-apply the given pending filters
|
||||
Object.entries(nextPending).forEach(([key, values]) => {
|
||||
const nonEmpty = values.filter(Boolean);
|
||||
if (nonEmpty.length > 0) {
|
||||
params.set(key, nonEmpty.join(","));
|
||||
@@ -203,6 +215,10 @@ export const useFilterBatch = (
|
||||
router.push(targetUrl, { scroll: false });
|
||||
};
|
||||
|
||||
const applyAll = () => {
|
||||
buildAndPush(pendingFilters);
|
||||
};
|
||||
|
||||
const discardAll = () => {
|
||||
const applied = deriveAppliedFromUrl(
|
||||
new URLSearchParams(searchParams.toString()),
|
||||
@@ -230,6 +246,39 @@ export const useFilterBatch = (
|
||||
setPendingFilters({});
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears ALL batch-managed filters and immediately navigates (router.push).
|
||||
*
|
||||
* Works around the async gap between clearAll() + applyAll(): instead of
|
||||
* setting pending to `{}` and then calling applyAll() (which would still
|
||||
* read the old pendingFilters from the closure), this function builds the
|
||||
* target URL directly from an empty pending state and pushes it in one step.
|
||||
* defaultParams (e.g. filter[muted]=false) are applied as usual.
|
||||
*/
|
||||
const clearAndApply = () => {
|
||||
setPendingFilters({});
|
||||
buildAndPush({});
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes only the specified filter keys from pending state and immediately
|
||||
* navigates (router.push) with the remaining filters + defaultParams.
|
||||
*
|
||||
* Used by the pills strip "Clear all" to remove pill-visible filters (severity,
|
||||
* status, delta, region, service, etc.) without touching provider/account selectors.
|
||||
*/
|
||||
const clearKeys = (keys: string[]) => {
|
||||
const normalizedKeys = keys.map((k) =>
|
||||
k.startsWith("filter[") ? k : `filter[${k}]`,
|
||||
);
|
||||
const nextPending: PendingFilters = { ...pendingFilters };
|
||||
normalizedKeys.forEach((k) => {
|
||||
delete nextPending[k];
|
||||
});
|
||||
setPendingFilters(nextPending);
|
||||
buildAndPush(nextPending);
|
||||
};
|
||||
|
||||
const getFilterValue = (key: string): string[] => {
|
||||
const filterKey = key.startsWith("filter[") ? key : `filter[${key}]`;
|
||||
return pendingFilters[filterKey] ?? [];
|
||||
@@ -249,6 +298,8 @@ export const useFilterBatch = (
|
||||
applyAll,
|
||||
discardAll,
|
||||
clearAll,
|
||||
clearAndApply,
|
||||
clearKeys,
|
||||
removePending,
|
||||
hasChanges,
|
||||
changeCount,
|
||||
|
||||
Reference in New Issue
Block a user