Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49104f39df | ||
|
|
4d13e8432e | ||
|
|
5f24bec9fe | ||
|
|
afefb8f333 | ||
|
|
2b81fdcc04 | ||
|
|
db298c1d48 | ||
|
|
2877c3d6c0 | ||
|
|
ee64c17108 | ||
|
|
a610314eba | ||
|
|
301edea7ce | ||
|
|
c89d900aae | ||
|
|
4cfb4eeb96 | ||
|
|
f19478f2f6 | ||
|
|
2f11b16299 | ||
|
|
654d2c9f17 | ||
|
|
2721d42594 | ||
|
|
bd1956446d | ||
|
|
d26d7cf91a | ||
|
|
4c20bf1fac | ||
|
|
9dc53ffc60 | ||
|
|
95642fb220 | ||
|
|
6449f3a592 | ||
|
|
91e6cb798d | ||
|
|
bcd37988b5 | ||
|
|
7a64e1a1d1 | ||
|
|
39c85ffb77 | ||
|
|
301ca50541 | ||
|
|
f299e1d9ac | ||
|
|
465e35bf54 | ||
|
|
829af2e3f7 | ||
|
|
51c65bdc69 | ||
|
|
d0c088dd0a | ||
|
|
411d112165 | ||
|
|
8a4cc8780d | ||
|
|
cd4d2a27e3 | ||
|
|
9898cf7364 | ||
|
|
83d8cfa829 | ||
|
|
5202a68cf0 | ||
|
|
e3a3acc799 | ||
|
|
3e000faa31 | ||
|
|
f39c92b8f8 | ||
|
|
823efc5ab1 | ||
|
|
75d7fa5006 | ||
|
|
db25484ccb | ||
|
|
3da4209ee7 | ||
|
|
acb6ff0425 | ||
|
|
ba564af4f4 | ||
|
|
0b9791ffdc | ||
|
|
f6defefb58 | ||
|
|
f3224d0988 | ||
|
|
450e6ba553 | ||
|
|
2cd93fe119 | ||
|
|
f807b22ea6 | ||
|
|
b6e9967da6 | ||
|
|
16e62f7514 | ||
|
|
13ce9436b3 | ||
|
|
d3ced63397 | ||
|
|
758b696ca5 | ||
|
|
0d3ce45374 | ||
|
|
f35666ff0a | ||
|
|
0758c3585d | ||
|
|
dd882c70e7 | ||
|
|
d05c9fbb31 | ||
|
|
7bde42ffb9 | ||
|
|
0d3df0fd0b | ||
|
|
ab996417e6 | ||
|
|
5f109bc00e | ||
|
|
472e04f4cc | ||
|
|
b848aace33 | ||
|
|
94c20eb9fe | ||
|
|
02df22ca19 | ||
|
|
de64df11b9 | ||
|
|
37ebd9b6fd | ||
|
|
a28487cbff | ||
|
|
68471d2a0e | ||
|
|
d41b2eaa0f | ||
|
|
b480907484 | ||
|
|
6d7bc8a86e | ||
|
|
8bfca81e4b | ||
|
|
931612443a | ||
|
|
3074f02a63 | ||
|
|
5cfc22040a | ||
|
|
48ba1692e1 | ||
|
|
a8b12813f9 | ||
|
|
85c36bb812 | ||
|
|
ce037318cd | ||
|
|
356036fe1f | ||
|
|
286685a4f3 | ||
|
|
9daca2e4df | ||
|
|
3ca3a977a9 | ||
|
|
561a1390be |
@@ -158,7 +158,7 @@ SENTRY_RELEASE=local
|
||||
# REO_DEV_CLIENT_ID=
|
||||
|
||||
#### Prowler release version ####
|
||||
NEXT_PUBLIC_PROWLER_RELEASE_VERSION=v5.39.0
|
||||
NEXT_PUBLIC_PROWLER_RELEASE_VERSION=v5.40.0
|
||||
|
||||
# Social login credentials
|
||||
SOCIAL_GOOGLE_OAUTH_CALLBACK_URL="${AUTH_URL}/api/auth/callback/google"
|
||||
|
||||
@@ -64,7 +64,7 @@ runs:
|
||||
scanners: 'vuln'
|
||||
ignore-unfixed: 'true' # A finding with no available fix is not actionable, so it must not gate
|
||||
timeout: '5m'
|
||||
version: 'v0.72.0'
|
||||
version: 'v0.74.0'
|
||||
# Not trivyignores: that input drops the .yaml extension Trivy parses by.
|
||||
env:
|
||||
TRIVY_IGNOREFILE: '.trivyignore.yaml'
|
||||
@@ -81,7 +81,7 @@ runs:
|
||||
scanners: 'vuln'
|
||||
ignore-unfixed: 'true' # A finding with no available fix is not actionable, so it must not gate
|
||||
timeout: '5m'
|
||||
version: 'v0.72.0'
|
||||
version: 'v0.74.0'
|
||||
# Not trivyignores: that input drops the .yaml extension Trivy parses by.
|
||||
env:
|
||||
TRIVY_IGNOREFILE: '.trivyignore.yaml'
|
||||
|
||||
@@ -199,7 +199,7 @@ You MUST structure your response using this EXACT format. Do NOT include anythin
|
||||
|
||||
### For Check Logic Bug
|
||||
|
||||
```
|
||||
```markdown
|
||||
### AI Assessment [Experimental]: Check Logic Bug
|
||||
|
||||
**Component**: {component from issue template}
|
||||
@@ -297,7 +297,7 @@ Write tests FIRST (TDD). The skills contain all testing conventions and patterns
|
||||
|
||||
### For Bug (non-check)
|
||||
|
||||
```
|
||||
```markdown
|
||||
### AI Assessment [Experimental]: Bug
|
||||
|
||||
**Component**: {CLI/SDK | API | UI | Dashboard | MCP Server | Other}
|
||||
@@ -378,7 +378,7 @@ Write tests FIRST (TDD). The skills contain all testing conventions and patterns
|
||||
|
||||
### For Already Fixed
|
||||
|
||||
```
|
||||
```markdown
|
||||
### AI Assessment [Experimental]: Already Fixed
|
||||
|
||||
**Component**: {component}
|
||||
@@ -401,7 +401,7 @@ Upgrade to the latest version. Close the issue as resolved.
|
||||
|
||||
### For Feature Request
|
||||
|
||||
```
|
||||
```markdown
|
||||
### AI Assessment [Experimental]: Feature Request
|
||||
|
||||
**Component**: {component}
|
||||
@@ -419,7 +419,7 @@ Upgrade to the latest version. Close the issue as resolved.
|
||||
|
||||
### For Not a Bug
|
||||
|
||||
```
|
||||
```markdown
|
||||
### AI Assessment [Experimental]: Not a Bug
|
||||
|
||||
**Component**: {component}
|
||||
@@ -440,7 +440,7 @@ Upgrade to the latest version. Close the issue as resolved.
|
||||
|
||||
### For Needs More Information
|
||||
|
||||
```
|
||||
```markdown
|
||||
### AI Assessment [Experimental]: Needs More Information
|
||||
|
||||
**Component**: {component or "Unknown"}
|
||||
|
||||
@@ -52,6 +52,16 @@ provider/alibabacloud:
|
||||
- any-glob-to-any-file: "prowler/providers/alibabacloud/**"
|
||||
- any-glob-to-any-file: "tests/providers/alibabacloud/**"
|
||||
|
||||
provider/huaweicloud:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "prowler/providers/huaweicloud/**"
|
||||
- any-glob-to-any-file: "tests/providers/huaweicloud/**"
|
||||
|
||||
provider/image:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "prowler/providers/image/**"
|
||||
- any-glob-to-any-file: "tests/providers/image/**"
|
||||
|
||||
provider/cloudflare:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "prowler/providers/cloudflare/**"
|
||||
@@ -82,6 +92,11 @@ provider/linode:
|
||||
- any-glob-to-any-file: "prowler/providers/linode/**"
|
||||
- any-glob-to-any-file: "tests/providers/linode/**"
|
||||
|
||||
provider/stackit:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: "prowler/providers/stackit/**"
|
||||
- any-glob-to-any-file: "tests/providers/stackit/**"
|
||||
|
||||
github_actions:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ".github/workflows/*"
|
||||
|
||||
@@ -8,11 +8,11 @@ These JSON templates are used with the `slackapi/slack-github-action` using the
|
||||
|
||||
### Available Templates
|
||||
|
||||
**Container Releases**
|
||||
#### Container Releases
|
||||
- `container-release-started.json`: Simple one-line notification when container push starts
|
||||
- `container-release-completed.json`: Simple one-line notification when container release completes
|
||||
|
||||
**Deployments**
|
||||
#### Deployments
|
||||
- `deployment-started.json`: Deployment start notification with Block Kit formatting
|
||||
- `deployment-completed.json`: Deployment completion notification (updates the start message)
|
||||
|
||||
@@ -416,17 +416,17 @@ For deployments that start with one message and update it with the final status:
|
||||
### Container Release (Simple One-Line)
|
||||
|
||||
**Start message:**
|
||||
```
|
||||
```text
|
||||
API container release 4.5.0 push started... View run
|
||||
```
|
||||
|
||||
**Completion message (success):**
|
||||
```
|
||||
```text
|
||||
[✓] API container release 4.5.0 push completed successfully! View run
|
||||
```
|
||||
|
||||
**Completion message (failure):**
|
||||
```
|
||||
```text
|
||||
[✗] API container release 4.5.0 push failed View run
|
||||
```
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ jobs:
|
||||
with:
|
||||
files: |
|
||||
api/**
|
||||
.trivyignore.yaml
|
||||
.github/actions/trivy-scan/**
|
||||
.github/actions/grype-scan/**
|
||||
.grype.yaml
|
||||
|
||||
@@ -61,6 +61,7 @@ jobs:
|
||||
api.github.com:443
|
||||
github.com:443
|
||||
objects.githubusercontent.com:443
|
||||
release-assets.githubusercontent.com:443
|
||||
pypi.org:443
|
||||
files.pythonhosted.org:443
|
||||
|
||||
|
||||
@@ -55,6 +55,10 @@ jobs:
|
||||
# Pin must match .pre-commit-config.yaml so prek and CI behave identically.
|
||||
# pnpm dlx doesn't accept --ignore-scripts as a flag; the env var
|
||||
# disables postinstall scripts on transitives the same way.
|
||||
#
|
||||
# Files come from `git ls-files` because markdownlint doesn't traverse
|
||||
# dot-directories, so `.github/**/*.md` went unlinted.
|
||||
# `.markdownlintignore` still applies to the listed paths.
|
||||
env:
|
||||
pnpm_config_ignore_scripts: 'true'
|
||||
run: pnpm dlx markdownlint-cli@0.45.0 '**/*.md'
|
||||
run: git ls-files -z '*.md' | xargs -0 -r pnpm dlx markdownlint-cli@0.45.0 --
|
||||
|
||||
@@ -114,6 +114,8 @@ jobs:
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
auth.docker.io:443
|
||||
dl-cdn.alpinelinux.org:443
|
||||
dualstack.j.sni.global.fastly.net:443
|
||||
files.pythonhosted.org:443
|
||||
ghcr.io:443
|
||||
github.com:443
|
||||
|
||||
@@ -81,6 +81,8 @@ jobs:
|
||||
pkg-containers.githubusercontent.com:443
|
||||
files.pythonhosted.org:443
|
||||
pypi.org:443
|
||||
dl-cdn.alpinelinux.org:443
|
||||
dualstack.j.sni.global.fastly.net:443
|
||||
api.github.com:443
|
||||
mirror.gcr.io:443
|
||||
check.trivy.dev:443
|
||||
@@ -103,6 +105,7 @@ jobs:
|
||||
with:
|
||||
files: |
|
||||
mcp_server/**
|
||||
.trivyignore.yaml
|
||||
.github/actions/trivy-scan/**
|
||||
.github/actions/grype-scan/**
|
||||
.grype.yaml
|
||||
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
|
||||
- name: Publish prowler-mcp package to PyPI
|
||||
if: steps.pypi-check.outputs.skip != 'true'
|
||||
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
||||
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
||||
with:
|
||||
packages-dir: ${{ env.WORKING_DIRECTORY }}/dist/
|
||||
print-hash: true
|
||||
|
||||
@@ -35,6 +35,7 @@ jobs:
|
||||
api.github.com:443
|
||||
github.com:443
|
||||
objects.githubusercontent.com:443
|
||||
release-assets.githubusercontent.com:443
|
||||
pypi.org:443
|
||||
files.pythonhosted.org:443
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ on:
|
||||
- 'Dockerfile*'
|
||||
- 'pyproject.toml'
|
||||
- 'uv.lock'
|
||||
- '.trivyignore.yaml'
|
||||
- '.github/workflows/sdk-container-checks.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -116,6 +117,7 @@ jobs:
|
||||
Dockerfile*
|
||||
pyproject.toml
|
||||
uv.lock
|
||||
.trivyignore.yaml
|
||||
.github/workflows/sdk-container-checks.yml
|
||||
.github/actions/trivy-scan/**
|
||||
.github/actions/grype-scan/**
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
name: 'SDK: Package Checks'
|
||||
|
||||
# Rehearses the PyPI release on every packaging change and once a week, from the
|
||||
# consumer's side. Two incidents this guards against:
|
||||
#
|
||||
# - 5.38.0 shipped an unsatisfiable Requires-Dist (cryptography==50.0.0 while
|
||||
# alibabacloud-tea-openapi and pyopenssl cap it below 49). A [tool.uv] override hid
|
||||
# the conflict inside the repo; pip could not install the wheel and silently
|
||||
# resolved `pip install prowler` to 5.37.1 for a week.
|
||||
# - 5.39.0 never published: an unpinned build backend started emitting core metadata
|
||||
# 2.5 and the twine bundled in the publish action rejected it.
|
||||
#
|
||||
# Both were only detectable at release time because nothing built and installed the
|
||||
# artifact earlier. The weekly run also catches releases yanked from PyPI after we
|
||||
# pinned them (zstd 1.5.7.3, "buggy - not thread safe", sat in uv.lock for months).
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'v5.*'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'v5.*'
|
||||
schedule:
|
||||
# Monday 06:00 UTC. Yanks and upstream releases happen without a commit here.
|
||||
- cron: '0 6 * * 1'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
# Must equal the twine bundled in the pypa/gh-action-pypi-publish pin used by
|
||||
# sdk-pypi-release.yml (requirements/runtime.txt in that repo at the pinned tag).
|
||||
# A metadata check that passes here must pass there.
|
||||
TWINE_VERSION: '7.0.0'
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
if: github.repository == 'prowler-cloud/prowler'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
# Scheduled and manual runs always execute; pushes and PRs only when a packaging
|
||||
# input changed. Jobs skipped this way still report success to branch protection.
|
||||
run: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || steps.filter.outputs.any_changed == 'true' }}
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
||||
with:
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
github.com:443
|
||||
api.github.com:443
|
||||
|
||||
- name: Checkout repository
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# zizmor: ignore[artipacked]
|
||||
persist-credentials: true # Required by tj-actions/changed-files to fetch PR branch
|
||||
|
||||
- name: Detect packaging changes
|
||||
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
||||
id: filter
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files: |
|
||||
pyproject.toml
|
||||
uv.lock
|
||||
README.md
|
||||
util/replicate_pypi_package.py
|
||||
util/check_yanked_pins.py
|
||||
api/pyproject.toml
|
||||
api/uv.lock
|
||||
mcp_server/pyproject.toml
|
||||
mcp_server/uv.lock
|
||||
.github/workflows/sdk-package-checks.yml
|
||||
.github/workflows/sdk-pypi-release.yml
|
||||
.github/actions/setup-python-uv/**
|
||||
|
||||
install-from-wheel:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version:
|
||||
- '3.10'
|
||||
- '3.11'
|
||||
- '3.12'
|
||||
- '3.13'
|
||||
package:
|
||||
- 'prowler'
|
||||
include:
|
||||
# prowler-cloud is the same tree renamed by util/replicate_pypi_package.py;
|
||||
# one Python is enough to prove the rename and its build still work.
|
||||
- python-version: '3.12'
|
||||
package: 'prowler-cloud'
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
||||
with:
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
github.com:443
|
||||
api.github.com:443
|
||||
release-assets.githubusercontent.com:443
|
||||
pypi.org:443
|
||||
files.pythonhosted.org:443
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Python with uv
|
||||
uses: ./.github/actions/setup-python-uv
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
install-dependencies: 'false'
|
||||
|
||||
- name: Rename package to prowler-cloud
|
||||
if: matrix.package == 'prowler-cloud'
|
||||
run: |
|
||||
pip install --no-cache-dir toml
|
||||
python util/replicate_pypi_package.py
|
||||
|
||||
- name: Build sdist and wheel
|
||||
run: uv build
|
||||
|
||||
- name: Check metadata with the release workflow's twine
|
||||
run: uvx --from "twine==${TWINE_VERSION}" twine check --strict dist/*
|
||||
|
||||
- name: Install the wheel with pip into a clean virtualenv
|
||||
# Plain pip, --isolated, from outside the repo: consumers never see [tool.uv]
|
||||
# override-dependencies or constraint-dependencies, so neither does this step.
|
||||
run: |
|
||||
python -m venv "${RUNNER_TEMP}/consumer"
|
||||
"${RUNNER_TEMP}/consumer/bin/python" -m pip install --quiet --upgrade pip
|
||||
cd "${RUNNER_TEMP}"
|
||||
"${RUNNER_TEMP}/consumer/bin/python" -m pip install --isolated --no-cache-dir "${GITHUB_WORKSPACE}"/dist/*.whl
|
||||
|
||||
- name: Smoke test the installed CLI
|
||||
run: |
|
||||
cd "${RUNNER_TEMP}"
|
||||
"${RUNNER_TEMP}/consumer/bin/prowler" --version
|
||||
# Loads every AWS check module from the installed wheel: catches files missing
|
||||
# from the package. grep fails the step if the summary line never appears.
|
||||
"${RUNNER_TEMP}/consumer/bin/prowler" aws --list-checks | grep 'available checks'
|
||||
|
||||
pinned-releases-not-yanked:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.run == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
||||
with:
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
github.com:443
|
||||
api.github.com:443
|
||||
release-assets.githubusercontent.com:443
|
||||
pypi.org:443
|
||||
files.pythonhosted.org:443
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Check every pinned and locked release against PyPI
|
||||
run: python util/check_yanked_pins.py . api mcp_server
|
||||
@@ -84,8 +84,18 @@ jobs:
|
||||
- name: Build Prowler package
|
||||
run: uv build
|
||||
|
||||
- name: Verify the wheel installs with pip
|
||||
# Same check as "SDK: Package Checks", repeated on the exact artifact about to be
|
||||
# published. Plain pip, --isolated, from outside the repo: an unsatisfiable
|
||||
# Requires-Dist fails here instead of on users' machines (5.38.0 shipped one).
|
||||
run: |
|
||||
python -m venv "${RUNNER_TEMP}/consumer"
|
||||
"${RUNNER_TEMP}/consumer/bin/python" -m pip install --quiet --upgrade pip
|
||||
cd "${RUNNER_TEMP}"
|
||||
"${RUNNER_TEMP}/consumer/bin/python" -m pip install --isolated --no-cache-dir --dry-run "${GITHUB_WORKSPACE}"/dist/*.whl
|
||||
|
||||
- name: Publish Prowler package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
||||
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
||||
with:
|
||||
print-hash: true
|
||||
|
||||
@@ -128,7 +138,17 @@ jobs:
|
||||
- name: Build prowler-cloud package
|
||||
run: uv build
|
||||
|
||||
- name: Verify the wheel installs with pip
|
||||
# Same check as "SDK: Package Checks", repeated on the exact artifact about to be
|
||||
# published. Plain pip, --isolated, from outside the repo: an unsatisfiable
|
||||
# Requires-Dist fails here instead of on users' machines (5.38.0 shipped one).
|
||||
run: |
|
||||
python -m venv "${RUNNER_TEMP}/consumer"
|
||||
"${RUNNER_TEMP}/consumer/bin/python" -m pip install --quiet --upgrade pip
|
||||
cd "${RUNNER_TEMP}"
|
||||
"${RUNNER_TEMP}/consumer/bin/python" -m pip install --isolated --no-cache-dir --dry-run "${GITHUB_WORKSPACE}"/dist/*.whl
|
||||
|
||||
- name: Publish prowler-cloud package to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
||||
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
||||
with:
|
||||
print-hash: true
|
||||
|
||||
@@ -57,6 +57,7 @@ jobs:
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
github.com:443
|
||||
release-assets.githubusercontent.com:443
|
||||
pypi.org:443
|
||||
files.pythonhosted.org:443
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ jobs:
|
||||
with:
|
||||
files: |
|
||||
ui/**
|
||||
.trivyignore.yaml
|
||||
.github/actions/trivy-scan/**
|
||||
.github/actions/grype-scan/**
|
||||
.grype.yaml
|
||||
|
||||
@@ -9,6 +9,14 @@ ignore:
|
||||
|
||||
# Modules compiled into the Trivy binary we ship.
|
||||
# Only a Trivy rebuild by its vendor can change these; the version is pinned in our Dockerfile.
|
||||
# CVE-2026-71556 is the same temporary exception documented in .trivyignore.yaml:
|
||||
# Trivy 0.73.0 still embeds go-git 5.19.1, while the 5.19.2 fix is merged only on
|
||||
# Trivy main. Remove this entry with the Trivy exception by 2026-09-15.
|
||||
# https://github.com/aquasecurity/trivy/blob/v0.73.0/go.mod#L46
|
||||
# https://github.com/aquasecurity/trivy/commit/a2edba9a03987ba0d2ebc8212c1a9a1e6979497b
|
||||
- vulnerability: CVE-2026-71556
|
||||
package:
|
||||
name: github.com/go-git/go-git/v5
|
||||
- vulnerability: CVE-2026-56852
|
||||
package:
|
||||
name: golang.org/x/text
|
||||
@@ -25,6 +33,19 @@ ignore:
|
||||
package:
|
||||
name: Microsoft.Bcl.Memory
|
||||
|
||||
# The .NET runtime bundled inside the PowerShell tarball the Dockerfile pins.
|
||||
# CVE-2026-62901 is the same temporary exception documented in .trivyignore.yaml:
|
||||
# fixed in .NET 9.0.19 / 10.0.11 (2026-08-11), but no published PowerShell release
|
||||
# ships a patched runtime yet (7.5.9 bundles 9.0.18; 7.6.4 bundles 10.0.x < 10.0.11).
|
||||
# pwsh runs only local M365 module cmdlets; nothing listens for inbound WebSocket
|
||||
# connections. Remove with the Trivy exception by 2026-09-15.
|
||||
- vulnerability: CVE-2026-62901
|
||||
package:
|
||||
name: Microsoft.NETCore.App.Runtime.linux-x64
|
||||
- vulnerability: CVE-2026-62901
|
||||
package:
|
||||
name: Microsoft.NETCore.App.Runtime.linux-arm64
|
||||
|
||||
|
||||
# The CPython interpreter, compiled into the official base image.
|
||||
# TEMPORARY, unlike the entries above: moving to Python 3.13 clears seven of these, and
|
||||
|
||||
@@ -118,8 +118,36 @@ vulnerabilities:
|
||||
- "pkg:npm/ip-address"
|
||||
expired_at: 2027-01-31
|
||||
|
||||
# CVE-2026-62901 is a DoS in System.Net.WebSockets (unchecked input for loop condition,
|
||||
# CWE-606), fixed in .NET 9.0.19 / 10.0.11 (published 2026-08-11). The vulnerable runtime
|
||||
# ships inside the PowerShell tarball the Dockerfile pins: 7.5.9 is the latest 7.5.x and
|
||||
# bundles .NET 9.0.18; 7.6.4 bundles .NET 10.0.x < 10.0.11, so no published PowerShell
|
||||
# release contains the fix yet. Prowler only invokes pwsh locally to run M365 module
|
||||
# cmdlets; the image does not accept inbound WebSocket connections, so the DoS path is
|
||||
# not reachable from the network. Remove this temporary suppression as soon as a
|
||||
# PowerShell release shipping .NET 9.0.19+ is available.
|
||||
- id: CVE-2026-62901
|
||||
purls:
|
||||
- "pkg:nuget/Microsoft.NETCore.App.Runtime.linux-x64"
|
||||
- "pkg:nuget/Microsoft.NETCore.App.Runtime.linux-arm64"
|
||||
expired_at: 2026-09-15
|
||||
|
||||
# Modules compiled into the Trivy binary the images ship. The binary is pinned by version
|
||||
# and verified by checksum in the Dockerfile; only a rebuild by its vendor moves these.
|
||||
# CVE-2026-71556 affects go-git worktree operations that can follow symlinks outside a
|
||||
# cloned repository. Trivy 0.73.0, the latest published release and the version the
|
||||
# images ship, still pins that vulnerable version:
|
||||
# https://github.com/aquasecurity/trivy/blob/v0.73.0/go.mod#L46
|
||||
# Trivy main already contains the 5.19.2 fix, but no published release includes it yet:
|
||||
# https://github.com/aquasecurity/trivy/commit/a2edba9a03987ba0d2ebc8212c1a9a1e6979497b
|
||||
# Prowler invokes Trivy only with `fs` on an existing local path or with `image`; it does
|
||||
# not ask Trivy to clone or mutate a Git worktree, so the affected path is not reachable.
|
||||
# Remove this temporary suppression as soon as a fixed Trivy release is available.
|
||||
- id: CVE-2026-71556
|
||||
purls:
|
||||
- "pkg:golang/github.com/go-git/go-git/v5"
|
||||
expired_at: 2026-09-15
|
||||
|
||||
- id: CVE-2026-56852
|
||||
purls:
|
||||
- "pkg:golang/golang.org/x/text"
|
||||
@@ -136,8 +164,3 @@ vulnerabilities:
|
||||
purls:
|
||||
- "pkg:golang/oras.land/oras-go/v2"
|
||||
expired_at: 2026-12-31
|
||||
- id: CVE-2026-39822
|
||||
purls:
|
||||
- "pkg:golang/stdlib"
|
||||
expired_at: 2026-12-31
|
||||
|
||||
|
||||
@@ -8,24 +8,34 @@ ENV POWERSHELL_VERSION=${POWERSHELL_VERSION}
|
||||
# Opt out of PowerShell telemetry (Application Insights -> dc.services.visualstudio.com)
|
||||
ENV POWERSHELL_TELEMETRY_OPTOUT=1
|
||||
|
||||
ARG TRIVY_VERSION=0.72.0
|
||||
ARG TRIVY_VERSION=0.74.0
|
||||
ENV TRIVY_VERSION=${TRIVY_VERSION}
|
||||
|
||||
ARG ZIZMOR_VERSION=1.24.1
|
||||
ENV ZIZMOR_VERSION=${ZIZMOR_VERSION}
|
||||
|
||||
# Pinned here, not fetched with the artefact: a compromised release ships its own checksum.
|
||||
ARG TRIVY_SHA256_AMD64=bbb64b9695866ce4a7a8f5c9592002c5961cab378577fa3f8a040df362b9b2ea
|
||||
ARG TRIVY_SHA256_ARM64=2ca2c023109c2db6b2b77366b6717291452d4531167377d95c79547f0c8e3467
|
||||
ARG TRIVY_SHA256_AMD64=2ae6fe3ee734b7fdf11335663e18c75ea12dccc76062f09f164a3b0f8be4371a
|
||||
ARG TRIVY_SHA256_ARM64=b94ce1976bbf3c15b514b605ee88be7c6d94a29be2302847ff01cb794d47aad5
|
||||
ARG POWERSHELL_SHA256_AMD64=492ff26bb958336bf61e597ce19e07648b4003bd2a08659e02f0e3e0446ebfe0
|
||||
ARG POWERSHELL_SHA256_ARM64=2503b71da3e83635592b092df59a0aca4c3606b4d9b068217bb00be989cb0d56
|
||||
ARG ZIZMOR_SHA256_AMD64=a8000f3c683319a523d3b20df0e75457ba591f049cfcbfa98966631b56733c03
|
||||
ARG ZIZMOR_SHA256_ARM64=d66e37ef8a375fb07939c630ebf9709a6e0f20242bdc3faf672a7ed97e0b768d
|
||||
|
||||
# High CVEs fixed in Debian trixie-security but not yet in the pinned base image:
|
||||
# openssl/libssl3t64/openssl-provider-legacy 3.5.7-1~deb13u2 CVE-2026-14456,
|
||||
# -14457, -18798, -54874, -63072, -63073, -63074, -63075, -63076, -75803
|
||||
# (image ships 3.5.6-1~deb13u2)
|
||||
# Taken as a targeted --only-upgrade rather than by moving the digest: the newest
|
||||
# published python:3.12-slim-trixie carries the same vulnerable version. The three
|
||||
# packages are all built from openssl and are flagged separately, so all are named.
|
||||
# Drop them once the base image ships 3.5.7-1~deb13u2 or later.
|
||||
# hadolint ignore=DL3008
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
wget libicu76 libunwind8 libssl3 libcurl4 ca-certificates apt-transport-https gnupg \
|
||||
build-essential pkg-config libzstd-dev zlib1g-dev \
|
||||
&& apt-get install -y --no-install-recommends --only-upgrade \
|
||||
util-linux libssl3t64 openssl openssl-provider-legacy \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install PowerShell
|
||||
|
||||
@@ -126,12 +126,12 @@ Every AWS provider scan will enqueue an Attack Paths ingestion job automatically
|
||||
|
||||
| Provider | Checks | Services | [Compliance Frameworks](https://docs.prowler.com/user-guide/compliance/tutorials/compliance) | [Categories](https://docs.prowler.com/user-guide/cli/tutorials/misc#categories) | Support | Interface |
|
||||
|---|---|---|---|---|---|---|
|
||||
| AWS | 621 | 86 | 47 | 19 | Official | UI, API, CLI |
|
||||
| AWS | 639 | 86 | 47 | 19 | Official | UI, API, CLI |
|
||||
| Azure | 191 | 22 | 21 | 16 | Official | UI, API, CLI |
|
||||
| GCP | 109 | 20 | 19 | 12 | Official | UI, API, CLI |
|
||||
| Kubernetes | 92 | 7 | 8 | 11 | Official | UI, API, CLI |
|
||||
| GitHub | 24 | 3 | 2 | 5 | Official | UI, API, CLI |
|
||||
| M365 | 111 | 10 | 6 | 10 | Official | UI, API, CLI |
|
||||
| M365 | 143 | 10 | 6 | 10 | Official | UI, API, CLI |
|
||||
| OCI | 52 | 14 | 5 | 10 | Official | UI, API, CLI |
|
||||
| Alibaba Cloud | 63 | 9 | 6 | 9 | Official | UI, API, CLI |
|
||||
| Cloudflare | 29 | 3 | 2 | 5 | Official | UI, API, CLI |
|
||||
@@ -147,7 +147,7 @@ Every AWS provider scan will enqueue an Attack Paths ingestion job automatically
|
||||
| Huawei Cloud [Contact us](https://prowler.com/contact) | 25 | 10 | 1 | 6 | Unofficial | CLI |
|
||||
| E2E Networks [Contact us](https://prowler.com/contact) | 27 | 6 | 0 | 2 | Unofficial | CLI |
|
||||
| Scaleway [Contact us](https://prowler.com/contact) | 1 | 1 | 1 | 1 | Unofficial | CLI |
|
||||
| StackIT [Contact us](https://prowler.com/contact) | 7 | 2 | 1 | 3 | Unofficial | CLI |
|
||||
| StackIT [Contact us](https://prowler.com/contact) | 8 | 2 | 1 | 3 | Unofficial | CLI |
|
||||
| NHN | 6 | 2 | 2 | 0 | Unofficial | CLI |
|
||||
|
||||
> [!Note]
|
||||
|
||||
@@ -4,6 +4,50 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
|
||||
<!-- changelog: release notes start -->
|
||||
|
||||
## [1.41.0] (Prowler v5.40.0)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- `FINDINGS_TABLE_PARTITION_MAX_AGE_MONTHS` is now applied in months instead of days, and negative values are rejected [(#12580)](https://github.com/prowler-cloud/prowler/pull/12580)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- `sqlparse` upgraded to 0.6.0, patching CVE-2026-54284, CVE-2026-59893, and CVE-2026-71491 [(#12509)](https://github.com/prowler-cloud/prowler/pull/12509)
|
||||
- `openssl`, `libssl3t64` and `openssl-provider-legacy` upgraded to 3.5.7-1~deb13u2 in the API container image, patching ten high OpenSSL CVEs [(#12549)](https://github.com/prowler-cloud/prowler/pull/12549)
|
||||
|
||||
---
|
||||
|
||||
## [1.40.1] (Prowler v5.39.1)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
- Bump alibabacloud-tea-openapi to 0.4.6, oci to 2.184.1 and pyopenssl to 26.4.0 to match the SDK; the cryptography override now names its actual blockers (azure-cli-core pins msal below 1.37, workos 8.3.0 requires cryptography 48) [(#12477)](https://github.com/prowler-cloud/prowler/pull/12477)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Pin zstd to 1.5.7.2; 1.5.7.3 was yanked from PyPI as not thread safe [(#12477)](https://github.com/prowler-cloud/prowler/pull/12477)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- Trivy from v0.72.0 to v0.73.0 in the container image, fixing HIGH CVE-2026-46600 in the bundled `golang.org/x/net` [(#12445)](https://github.com/prowler-cloud/prowler/pull/12445)
|
||||
- Trivy v0.74.0 and Debian util-linux 2.41.5-0+deb13u1 in the API container image, patching Go standard library vulnerabilities and CVE-2026-53615 [(#12470)](https://github.com/prowler-cloud/prowler/pull/12470)
|
||||
|
||||
---
|
||||
|
||||
## [1.40.0] (Prowler v5.39.0)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
- `GET /api/v1/users/me` membership relationships identify the active tenant with `meta.active` for JWT and API key authentication [(#12388)](https://github.com/prowler-cloud/prowler/pull/12388)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Tenant deletion no longer leaves memberships partially removed when exclusive-user cleanup fails [(#12379)](https://github.com/prowler-cloud/prowler/pull/12379)
|
||||
- `/api/v1/accounts/saml/{organization_slug}/acs/` rejects non-POST requests before SAML response processing [(#12393)](https://github.com/prowler-cloud/prowler/pull/12393)
|
||||
- Social login derives a valid user name when identity providers omit the profile name [(#12413)](https://github.com/prowler-cloud/prowler/pull/12413)
|
||||
|
||||
---
|
||||
|
||||
## [1.39.0] (Prowler v5.38.0)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
@@ -7,20 +7,28 @@ ENV POWERSHELL_VERSION=${POWERSHELL_VERSION}
|
||||
# Opt out of PowerShell telemetry (Application Insights -> dc.services.visualstudio.com)
|
||||
ENV POWERSHELL_TELEMETRY_OPTOUT=1
|
||||
|
||||
ARG TRIVY_VERSION=0.72.0
|
||||
ARG TRIVY_VERSION=0.74.0
|
||||
ENV TRIVY_VERSION=${TRIVY_VERSION}
|
||||
|
||||
ARG ZIZMOR_VERSION=1.24.1
|
||||
ENV ZIZMOR_VERSION=${ZIZMOR_VERSION}
|
||||
|
||||
# Pinned here, not fetched with the artefact: a compromised release ships its own checksum.
|
||||
ARG TRIVY_SHA256_AMD64=bbb64b9695866ce4a7a8f5c9592002c5961cab378577fa3f8a040df362b9b2ea
|
||||
ARG TRIVY_SHA256_ARM64=2ca2c023109c2db6b2b77366b6717291452d4531167377d95c79547f0c8e3467
|
||||
ARG TRIVY_SHA256_AMD64=2ae6fe3ee734b7fdf11335663e18c75ea12dccc76062f09f164a3b0f8be4371a
|
||||
ARG TRIVY_SHA256_ARM64=b94ce1976bbf3c15b514b605ee88be7c6d94a29be2302847ff01cb794d47aad5
|
||||
ARG POWERSHELL_SHA256_AMD64=492ff26bb958336bf61e597ce19e07648b4003bd2a08659e02f0e3e0446ebfe0
|
||||
ARG POWERSHELL_SHA256_ARM64=2503b71da3e83635592b092df59a0aca4c3606b4d9b068217bb00be989cb0d56
|
||||
ARG ZIZMOR_SHA256_AMD64=a8000f3c683319a523d3b20df0e75457ba591f049cfcbfa98966631b56733c03
|
||||
ARG ZIZMOR_SHA256_ARM64=d66e37ef8a375fb07939c630ebf9709a6e0f20242bdc3faf672a7ed97e0b768d
|
||||
|
||||
# High CVEs fixed in Debian trixie-security but not yet in the pinned base image:
|
||||
# openssl/libssl3t64/openssl-provider-legacy 3.5.7-1~deb13u2 CVE-2026-14456,
|
||||
# -14457, -18798, -54874, -63072, -63073, -63074, -63075, -63076, -75803
|
||||
# (image ships 3.5.6-1~deb13u2)
|
||||
# Taken as a targeted --only-upgrade rather than by moving the digest: the newest
|
||||
# published python:3.12-slim-trixie carries the same vulnerable version. The three
|
||||
# packages are all built from openssl and are flagged separately, so all are named.
|
||||
# Drop them once the base image ships 3.5.7-1~deb13u2 or later.
|
||||
# hadolint ignore=DL3008
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
wget \
|
||||
@@ -36,6 +44,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libtool \
|
||||
libxslt1-dev \
|
||||
python3-dev \
|
||||
&& apt-get install -y --no-install-recommends --only-upgrade \
|
||||
util-linux libssl3t64 openssl openssl-provider-legacy \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install PowerShell
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
`/api/v1/accounts/saml/{organization_slug}/acs/` rejects non-POST requests before SAML response processing
|
||||
@@ -1 +0,0 @@
|
||||
Tenant deletion no longer leaves memberships partially removed when exclusive-user cleanup fails
|
||||
@@ -1 +0,0 @@
|
||||
`GET /api/v1/users/me` membership relationships identify the active tenant with `meta.active` for JWT and API key authentication
|
||||
@@ -45,7 +45,7 @@ dependencies = [
|
||||
"gunicorn==26.0.0",
|
||||
"uvloop==0.22.1",
|
||||
"lxml==6.1.0",
|
||||
"prowler @ git+https://github.com/prowler-cloud/prowler.git@master",
|
||||
"prowler @ git+https://github.com/prowler-cloud/prowler.git@v5.40",
|
||||
"psycopg2-binary==2.9.9",
|
||||
"pytest-celery[redis] (==1.3.0)",
|
||||
"sentry-sdk[django] (==2.56.0)",
|
||||
@@ -61,7 +61,7 @@ dependencies = [
|
||||
"cartography (==0.138.1)",
|
||||
"gevent (==25.9.1)",
|
||||
"werkzeug (==3.1.7)",
|
||||
"sqlparse (==0.5.5)",
|
||||
"sqlparse (==0.6.0)",
|
||||
"fonttools (==4.62.1)",
|
||||
"uvicorn-worker (==0.4.0)"
|
||||
]
|
||||
@@ -71,7 +71,7 @@ name = "prowler-api"
|
||||
package-mode = false
|
||||
# Needed for the SDK compatibility
|
||||
requires-python = ">=3.11,<3.13"
|
||||
version = "1.40.0"
|
||||
version = "1.41.0"
|
||||
|
||||
# Shared ruff baseline (kept in sync with mcp_server/pyproject.toml).
|
||||
# target-version tracks this project's lowest supported Python.
|
||||
@@ -92,8 +92,7 @@ extend-select = [
|
||||
|
||||
[tool.uv]
|
||||
# Transitive pins matching master to avoid silent drift; bump deliberately.
|
||||
# workos and pyopenssl run ahead of master: the versions master pins cap cryptography
|
||||
# below 48, so both were bumped to versions that allow it (PROWLER-2310).
|
||||
# workos is api-only; pyopenssl matches master (PROWLER-2310).
|
||||
constraint-dependencies = [
|
||||
"about-time==4.2.1",
|
||||
"adal==1.2.7",
|
||||
@@ -130,7 +129,7 @@ constraint-dependencies = [
|
||||
"alibabacloud-sls20201230==5.9.0",
|
||||
"alibabacloud-sts20150401==1.1.6",
|
||||
"alibabacloud-tea==0.4.3",
|
||||
"alibabacloud-tea-openapi==0.4.5",
|
||||
"alibabacloud-tea-openapi==0.4.6",
|
||||
"alibabacloud-tea-util==0.3.14",
|
||||
"alibabacloud-tea-xml==0.0.3",
|
||||
"alibabacloud-vpc20160428==6.13.0",
|
||||
@@ -339,7 +338,7 @@ constraint-dependencies = [
|
||||
"nltk==3.9.4",
|
||||
"numpy==2.2.6",
|
||||
"oauthlib==3.3.1",
|
||||
"oci==2.183.0",
|
||||
"oci==2.184.1",
|
||||
"openai==1.109.1",
|
||||
"openstacksdk==4.2.0",
|
||||
"opentelemetry-api==1.39.1",
|
||||
@@ -380,7 +379,7 @@ constraint-dependencies = [
|
||||
"pylint==3.2.5",
|
||||
"pymsalruntime==0.18.1",
|
||||
"pynacl==1.6.2",
|
||||
"pyopenssl==26.2.0",
|
||||
"pyopenssl==26.4.0",
|
||||
"pyparsing==3.3.2",
|
||||
"pyreadline3==3.5.4",
|
||||
"pysocks==1.7.1",
|
||||
@@ -424,7 +423,7 @@ constraint-dependencies = [
|
||||
"six==1.17.0",
|
||||
"slack-sdk==3.39.0",
|
||||
"sniffio==1.3.1",
|
||||
"sqlparse==0.5.5",
|
||||
"sqlparse==0.6.0",
|
||||
"statsd==4.0.1",
|
||||
"std-uritemplate==2.0.8",
|
||||
"stevedore==5.6.0",
|
||||
@@ -458,7 +457,7 @@ constraint-dependencies = [
|
||||
"zipp==3.23.0",
|
||||
"zope-event==6.1",
|
||||
"zope-interface==8.2",
|
||||
"zstd==1.5.7.3"
|
||||
"zstd==1.5.7.2"
|
||||
]
|
||||
# prowler@master needs okta==3.4.2, but cartography 0.138.1 requires okta<1.0.0.
|
||||
# Attack Paths does not ingest Okta today, so override the Cartography
|
||||
@@ -485,7 +484,12 @@ constraint-dependencies = [
|
||||
# that request pyjwt[crypto] and leave cryptography (needed for RS256) only transitive.
|
||||
override-dependencies = [
|
||||
"okta==3.4.2",
|
||||
# alibabacloud-tea-openapi 0.4.5 caps cryptography below 49 and is the latest release.
|
||||
# prowler requires cryptography==50.0.0. Two api-only dependencies still cap it below
|
||||
# 49 and cannot move yet: msal, pinned exactly by azure-cli-core (2.83.0 -> 1.35.0b1,
|
||||
# 2.89.1 -> 1.36.0, both <49; cartography needs azure-cli-core), and workos 8.3.0
|
||||
# (~=48.0; workos 10.1.1+ needs ~=50.0 and is a separate SDK upgrade). This api is
|
||||
# deployed from this lock with `uv sync --locked`, so the override applies to what runs.
|
||||
# Remove when azure-cli-core pins msal>=1.37.0 and workos is on 10.x.
|
||||
"cryptography==50.0.0",
|
||||
"azure-mgmt-containerservice==34.1.0",
|
||||
"microsoft-kiota-abstractions==1.9.10",
|
||||
|
||||
@@ -12,11 +12,37 @@ from api.models import (
|
||||
UserRoleRelationship,
|
||||
)
|
||||
from api.utils import accept_invitation_for_user
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction
|
||||
from django.http import HttpResponseForbidden
|
||||
|
||||
|
||||
class ProwlerSocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||
@staticmethod
|
||||
def _get_social_account_name(extra_data: dict, email: str) -> str:
|
||||
name_field = User._meta.get_field("name")
|
||||
for value in (
|
||||
extra_data.get("name"),
|
||||
extra_data.get("login"),
|
||||
extra_data.get("username"),
|
||||
email,
|
||||
):
|
||||
if not isinstance(value, str):
|
||||
continue
|
||||
|
||||
candidate = value.strip()[: name_field.max_length].rstrip()
|
||||
if not candidate:
|
||||
continue
|
||||
|
||||
try:
|
||||
name_field.run_validators(candidate)
|
||||
except ValidationError:
|
||||
continue
|
||||
|
||||
return candidate
|
||||
|
||||
raise ValueError("Social account does not provide a valid user identity.")
|
||||
|
||||
@staticmethod
|
||||
def get_user_by_email(email: str):
|
||||
try:
|
||||
@@ -116,11 +142,8 @@ class ProwlerSocialAccountAdapter(DefaultSocialAccountAdapter):
|
||||
|
||||
if provider != "saml":
|
||||
# Handle other providers (e.g., GitHub, Google)
|
||||
user.name = self._get_social_account_name(extra, user.email)
|
||||
user.save(using=MainRouter.admin_db)
|
||||
social_account_name = extra.get("name")
|
||||
if social_account_name:
|
||||
user.name = social_account_name
|
||||
user.save(using=MainRouter.admin_db)
|
||||
|
||||
invitation_token = self._get_invitation_token(request)
|
||||
if invitation_token:
|
||||
|
||||
@@ -6,6 +6,7 @@ from api.rls import RowLevelSecurityConstraint
|
||||
from api.uuid_utils import datetime_to_uuid7
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from psqlextra.partitioning import (
|
||||
PostgresPartitioningError,
|
||||
PostgresPartitioningManager,
|
||||
@@ -153,10 +154,17 @@ class PostgresUUIDv7PartitioningStrategy(PostgresRangePartitioningStrategy):
|
||||
)
|
||||
|
||||
|
||||
def relative_days_or_none(value):
|
||||
if value is None:
|
||||
def relative_months_or_none(value):
|
||||
# A negative value would set the cutoff in the future and delete every
|
||||
# partition, so it is rejected rather than silently ignored.
|
||||
if value is not None and value < 0:
|
||||
raise ImproperlyConfigured(
|
||||
"FINDINGS_TABLE_PARTITION_MAX_AGE_MONTHS must not be negative; "
|
||||
"leave it unset or use 0 to keep partitions indefinitely"
|
||||
)
|
||||
if not value:
|
||||
return None
|
||||
return relativedelta(days=value)
|
||||
return relativedelta(months=value)
|
||||
|
||||
|
||||
#
|
||||
@@ -173,7 +181,7 @@ manager = PostgresPartitioningManager(
|
||||
months=settings.FINDINGS_TABLE_PARTITION_MONTHS
|
||||
),
|
||||
count=settings.FINDINGS_TABLE_PARTITION_COUNT,
|
||||
max_age=relative_days_or_none(
|
||||
max_age=relative_months_or_none(
|
||||
settings.FINDINGS_TABLE_PARTITION_MAX_AGE_MONTHS
|
||||
),
|
||||
name_format="%Y_%b",
|
||||
@@ -189,7 +197,7 @@ manager = PostgresPartitioningManager(
|
||||
months=settings.FINDINGS_TABLE_PARTITION_MONTHS
|
||||
),
|
||||
count=settings.FINDINGS_TABLE_PARTITION_COUNT,
|
||||
max_age=relative_days_or_none(
|
||||
max_age=relative_months_or_none(
|
||||
settings.FINDINGS_TABLE_PARTITION_MAX_AGE_MONTHS
|
||||
),
|
||||
name_format="%Y_%b",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Prowler API
|
||||
version: 1.40.0
|
||||
version: 1.41.0
|
||||
description: |-
|
||||
Prowler API specification.
|
||||
|
||||
|
||||
@@ -111,6 +111,110 @@ def _verify_local_email(user):
|
||||
)
|
||||
|
||||
|
||||
def test_social_account_name_falls_back_to_login_for_blank_name():
|
||||
adapter = ProwlerSocialAccountAdapter()
|
||||
|
||||
name = adapter._get_social_account_name(
|
||||
{"name": " ", "login": "octocat"},
|
||||
"verified@example.com",
|
||||
)
|
||||
|
||||
assert name == "octocat"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("provider_name", [None, "", " ", 123, ["name"]])
|
||||
def test_social_account_name_ignores_unusable_provider_names(provider_name):
|
||||
adapter = ProwlerSocialAccountAdapter()
|
||||
|
||||
name = adapter._get_social_account_name(
|
||||
{"name": provider_name, "login": "octocat"},
|
||||
"verified@example.com",
|
||||
)
|
||||
|
||||
assert name == "octocat"
|
||||
|
||||
|
||||
def test_social_account_name_uses_login_when_name_is_missing():
|
||||
adapter = ProwlerSocialAccountAdapter()
|
||||
|
||||
name = adapter._get_social_account_name(
|
||||
{"login": "octocat"},
|
||||
"verified@example.com",
|
||||
)
|
||||
|
||||
assert name == "octocat"
|
||||
|
||||
|
||||
def test_social_account_name_falls_back_to_username_then_email():
|
||||
adapter = ProwlerSocialAccountAdapter()
|
||||
|
||||
username_name = adapter._get_social_account_name(
|
||||
{"name": "ab", "login": None, "username": " monalisa "},
|
||||
"verified@example.com",
|
||||
)
|
||||
email_name = adapter._get_social_account_name({}, " verified@example.com ")
|
||||
|
||||
assert username_name == "monalisa"
|
||||
assert email_name == "verified@example.com"
|
||||
|
||||
|
||||
def test_social_account_name_trims_and_limits_provider_name():
|
||||
adapter = ProwlerSocialAccountAdapter()
|
||||
max_length = User._meta.get_field("name").max_length
|
||||
|
||||
trimmed_name = adapter._get_social_account_name(
|
||||
{"name": " Ada Lovelace "},
|
||||
"verified@example.com",
|
||||
)
|
||||
limited_name = adapter._get_social_account_name(
|
||||
{"name": "a" * (max_length + 1)},
|
||||
"verified@example.com",
|
||||
)
|
||||
|
||||
assert trimmed_name == "Ada Lovelace"
|
||||
assert limited_name == "a" * max_length
|
||||
|
||||
|
||||
def test_social_account_name_rejects_missing_identity():
|
||||
adapter = ProwlerSocialAccountAdapter()
|
||||
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="Social account does not provide a valid user identity",
|
||||
):
|
||||
adapter._get_social_account_name({}, "")
|
||||
|
||||
|
||||
def test_save_user_applies_normalized_social_account_name(rf):
|
||||
adapter = ProwlerSocialAccountAdapter()
|
||||
request = rf.post("/")
|
||||
request.session = {}
|
||||
sociallogin = MagicMock(spec=SocialLogin)
|
||||
sociallogin.provider = MagicMock()
|
||||
sociallogin.provider.id = "github"
|
||||
sociallogin.account = MagicMock()
|
||||
sociallogin.account.extra_data = {"name": None, "login": " octocat "}
|
||||
user = User(email="verified@example.com")
|
||||
user.save = MagicMock()
|
||||
invitation = SimpleNamespace(tenant_id="tenant-id")
|
||||
|
||||
with (
|
||||
patch("api.adapters.super") as mock_super,
|
||||
patch("api.adapters.transaction.atomic"),
|
||||
patch("api.adapters.write_db_alias"),
|
||||
patch.object(adapter, "_get_invitation_token", return_value="token"),
|
||||
patch(
|
||||
"api.adapters.accept_invitation_for_user",
|
||||
return_value=(invitation, True),
|
||||
),
|
||||
):
|
||||
mock_super.return_value.save_user.return_value = user
|
||||
saved_user = adapter.save_user(request, sociallogin)
|
||||
|
||||
assert saved_user.name == "octocat"
|
||||
assert request.prowler_invitation_token == "token"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestProwlerSocialAccountAdapter:
|
||||
def test_get_user_by_email_returns_user(self, create_test_user):
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
from datetime import UTC, datetime
|
||||
from itertools import islice
|
||||
|
||||
import pytest
|
||||
from api.partitions import (
|
||||
PostgresUUIDv7PartitioningStrategy,
|
||||
relative_months_or_none,
|
||||
)
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from psqlextra.partitioning import PostgresTimePartitionSize
|
||||
|
||||
|
||||
def build_strategy(max_age):
|
||||
return PostgresUUIDv7PartitioningStrategy(
|
||||
size=PostgresTimePartitionSize(months=1),
|
||||
count=1,
|
||||
start_date=datetime.now(UTC),
|
||||
max_age=max_age,
|
||||
name_format="%Y_%b",
|
||||
)
|
||||
|
||||
|
||||
class TestRelativeMonthsOrNone:
|
||||
@pytest.mark.parametrize("value", [None, 0])
|
||||
def test_unset_or_zero_keeps_partitions_indefinitely(self, value):
|
||||
assert relative_months_or_none(value) is None
|
||||
|
||||
@pytest.mark.parametrize("months", [1, 3, 12])
|
||||
def test_value_is_interpreted_as_months(self, months):
|
||||
assert relative_months_or_none(months) == relativedelta(months=months)
|
||||
|
||||
def test_value_is_not_interpreted_as_days(self):
|
||||
assert relative_months_or_none(12) != relativedelta(days=12)
|
||||
|
||||
def test_negative_is_rejected(self):
|
||||
with pytest.raises(ImproperlyConfigured):
|
||||
relative_months_or_none(-12)
|
||||
|
||||
|
||||
class TestToDelete:
|
||||
@pytest.mark.parametrize("max_age", [None, relative_months_or_none(0)])
|
||||
def test_nothing_is_deleted_without_max_age(self, max_age):
|
||||
strategy = build_strategy(max_age)
|
||||
|
||||
assert list(islice(strategy.to_delete(), 5)) == []
|
||||
|
||||
def test_first_deleted_partition_is_max_age_old(self):
|
||||
months = 3
|
||||
strategy = build_strategy(relative_months_or_none(months))
|
||||
|
||||
first = next(strategy.to_delete())
|
||||
|
||||
expected = strategy.get_start_datetime() - relativedelta(months=months)
|
||||
assert first.name() == expected.strftime("%Y_%b").lower()
|
||||
|
||||
def test_deleted_partitions_go_further_back_in_time(self):
|
||||
strategy = build_strategy(relative_months_or_none(3))
|
||||
|
||||
names = [p.name() for p in islice(strategy.to_delete(), 3)]
|
||||
starts = [datetime.strptime(n, "%Y_%b") for n in names]
|
||||
|
||||
assert starts == sorted(starts, reverse=True)
|
||||
@@ -45,7 +45,7 @@ constraints = [
|
||||
{ name = "alibabacloud-sls20201230", specifier = "==5.9.0" },
|
||||
{ name = "alibabacloud-sts20150401", specifier = "==1.1.6" },
|
||||
{ name = "alibabacloud-tea", specifier = "==0.4.3" },
|
||||
{ name = "alibabacloud-tea-openapi", specifier = "==0.4.5" },
|
||||
{ name = "alibabacloud-tea-openapi", specifier = "==0.4.6" },
|
||||
{ name = "alibabacloud-tea-util", specifier = "==0.3.14" },
|
||||
{ name = "alibabacloud-tea-xml", specifier = "==0.0.3" },
|
||||
{ name = "alibabacloud-vpc20160428", specifier = "==6.13.0" },
|
||||
@@ -254,7 +254,7 @@ constraints = [
|
||||
{ name = "nltk", specifier = "==3.9.4" },
|
||||
{ name = "numpy", specifier = "==2.2.6" },
|
||||
{ name = "oauthlib", specifier = "==3.3.1" },
|
||||
{ name = "oci", specifier = "==2.183.0" },
|
||||
{ name = "oci", specifier = "==2.184.1" },
|
||||
{ name = "openai", specifier = "==1.109.1" },
|
||||
{ name = "openstacksdk", specifier = "==4.2.0" },
|
||||
{ name = "opentelemetry-api", specifier = "==1.39.1" },
|
||||
@@ -295,7 +295,7 @@ constraints = [
|
||||
{ name = "pylint", specifier = "==3.2.5" },
|
||||
{ name = "pymsalruntime", specifier = "==0.18.1" },
|
||||
{ name = "pynacl", specifier = "==1.6.2" },
|
||||
{ name = "pyopenssl", specifier = "==26.2.0" },
|
||||
{ name = "pyopenssl", specifier = "==26.4.0" },
|
||||
{ name = "pyparsing", specifier = "==3.3.2" },
|
||||
{ name = "pyreadline3", specifier = "==3.5.4" },
|
||||
{ name = "pysocks", specifier = "==1.7.1" },
|
||||
@@ -339,7 +339,7 @@ constraints = [
|
||||
{ name = "six", specifier = "==1.17.0" },
|
||||
{ name = "slack-sdk", specifier = "==3.39.0" },
|
||||
{ name = "sniffio", specifier = "==1.3.1" },
|
||||
{ name = "sqlparse", specifier = "==0.5.5" },
|
||||
{ name = "sqlparse", specifier = "==0.6.0" },
|
||||
{ name = "statsd", specifier = "==4.0.1" },
|
||||
{ name = "std-uritemplate", specifier = "==2.0.8" },
|
||||
{ name = "stevedore", specifier = "==5.6.0" },
|
||||
@@ -373,7 +373,7 @@ constraints = [
|
||||
{ name = "zipp", specifier = "==3.23.0" },
|
||||
{ name = "zope-event", specifier = "==6.1" },
|
||||
{ name = "zope-interface", specifier = "==8.2" },
|
||||
{ name = "zstd", specifier = "==1.5.7.3" },
|
||||
{ name = "zstd", specifier = "==1.5.7.2" },
|
||||
]
|
||||
overrides = [
|
||||
{ name = "azure-mgmt-containerservice", specifier = "==34.1.0" },
|
||||
@@ -860,7 +860,7 @@ sdist = { url = "https://files.pythonhosted.org/packages/9a/7d/b22cb9a0d4f396ee0
|
||||
|
||||
[[package]]
|
||||
name = "alibabacloud-tea-openapi"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "alibabacloud-credentials" },
|
||||
@@ -869,9 +869,9 @@ dependencies = [
|
||||
{ name = "cryptography" },
|
||||
{ name = "darabonba-core" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3b/73/fb0c4d44759791ecdf269fc715c1e810fa1aba3981bfaaf8a01f61899296/alibabacloud_tea_openapi-0.4.5.tar.gz", hash = "sha256:75fa1f4360a46e41f5bf5f8d4917e52efb6f64885839bc1328c35590670c97b9", size = 26616, upload-time = "2026-07-14T13:15:39.364Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ab/34/1918a2d780676494365c7f945bfab397ecddb988054d78025bd26f438977/alibabacloud_tea_openapi-0.4.6.tar.gz", hash = "sha256:dafc32401712f5b21c12dc3d05ba887a91ad156d9b49a7662279f9fd90526fb2", size = 26742, upload-time = "2026-08-17T08:34:11.55Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/ec/6b368a10e9c2e8b1b394c69b96ac213ae66e8c4895e0baa1ffaf7178fd32/alibabacloud_tea_openapi-0.4.5-py3-none-any.whl", hash = "sha256:338979095c7beda80a5b413c31262892cafdc12069dde4ce4fc2e4f7ce0fc609", size = 33333, upload-time = "2026-07-14T13:15:38.365Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/00/2f534f5884e5f299d9cb3a1e8be2def8071bc6a6e2a192ba4ff2a8cd5e02/alibabacloud_tea_openapi-0.4.6-py3-none-any.whl", hash = "sha256:c9e1727b9fb2936f487d050fc3590c99f9f2065256dc3a927e5b61f414674ed6", size = 33448, upload-time = "2026-08-17T08:34:10.472Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4426,7 +4426,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "oci"
|
||||
version = "2.183.0"
|
||||
version = "2.184.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
@@ -4439,9 +4439,9 @@ dependencies = [
|
||||
{ name = "pytz" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1e/2a/77bd6cbf1c69b2f368fe3d6462d84369b0cba15e37ce713cdc08d459b95a/oci-2.183.0.tar.gz", hash = "sha256:ff572ef5f2030a788796bb509d257e6a41c6510ef9b4b6a75a079efd06e533ce", size = 17759723, upload-time = "2026-07-28T06:02:29.76Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/74/2d/fa5368cfabb868f4111c6978e8b5f66aa3a55076c40c1a59ac3081b0227b/oci-2.184.1.tar.gz", hash = "sha256:617dad69caf8dd6e521d224dbc3e8a8bc289906943a0214fd2c3419094e26435", size = 17990631, upload-time = "2026-08-11T11:01:26.194Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/de/8574b3e527996a099d196e87794a4652d91a0c3185fcc7fdbb5649b75a8a/oci-2.183.0-py3-none-any.whl", hash = "sha256:bd789c98a94d7c5ea08c20d11dcf68c9cd1ad479b134727d80a930b84387070b", size = 36133501, upload-time = "2026-07-28T06:02:18.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/63/5ae22e42aaf96a5da74dc2b9de449c78b4d7418cce621d5da723b3e49f32/oci-2.184.1-py3-none-any.whl", hash = "sha256:bd814e38a70da2190e721937455a08689ab13c0750bd2ef8dd0c98b2dc5a38ea", size = 36628063, upload-time = "2026-08-11T11:01:18.178Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4835,8 +4835,8 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "prowler"
|
||||
version = "5.38.0"
|
||||
source = { git = "https://github.com/prowler-cloud/prowler.git?rev=master#b3d174d0c1eb202ed7cb9a9daf0500683f4443be" }
|
||||
version = "5.40.0"
|
||||
source = { git = "https://github.com/prowler-cloud/prowler.git?rev=v5.40#4d13e8432e57289a188c2a12200dcd8437f35543" }
|
||||
dependencies = [
|
||||
{ name = "alibabacloud-actiontrail20200706" },
|
||||
{ name = "alibabacloud-credentials" },
|
||||
@@ -4928,14 +4928,17 @@ dependencies = [
|
||||
{ name = "stackit-iaas" },
|
||||
{ name = "stackit-objectstorage" },
|
||||
{ name = "stackit-resourcemanager" },
|
||||
{ name = "stackit-ske" },
|
||||
{ name = "tabulate" },
|
||||
{ name = "truststore" },
|
||||
{ name = "tzlocal" },
|
||||
{ name = "uuid6" },
|
||||
{ name = "zstandard" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prowler-api"
|
||||
version = "1.40.0"
|
||||
version = "1.41.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "cartography" },
|
||||
@@ -5035,12 +5038,12 @@ requires-dist = [
|
||||
{ name = "matplotlib", specifier = "==3.10.8" },
|
||||
{ name = "neo4j", specifier = "==6.1.0" },
|
||||
{ name = "openai", specifier = "==1.109.1" },
|
||||
{ name = "prowler", git = "https://github.com/prowler-cloud/prowler.git?rev=master" },
|
||||
{ name = "prowler", git = "https://github.com/prowler-cloud/prowler.git?rev=v5.40" },
|
||||
{ name = "psycopg2-binary", specifier = "==2.9.9" },
|
||||
{ name = "pytest-celery", extras = ["redis"], specifier = "==1.3.0" },
|
||||
{ name = "reportlab", specifier = "==4.4.10" },
|
||||
{ name = "sentry-sdk", extras = ["django"], specifier = "==2.56.0" },
|
||||
{ name = "sqlparse", specifier = "==0.5.5" },
|
||||
{ name = "sqlparse", specifier = "==0.6.0" },
|
||||
{ name = "uuid6", specifier = "==2024.7.10" },
|
||||
{ name = "uvicorn-worker", specifier = "==0.4.0" },
|
||||
{ name = "uvloop", specifier = "==0.22.1" },
|
||||
@@ -5426,15 +5429,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pyopenssl"
|
||||
version = "26.2.0"
|
||||
version = "26.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cryptography" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1a/51/27a5ad5f939d08f690a326ef9582cda7140555180db71695f6fb747d6a36/pyopenssl-26.2.0.tar.gz", hash = "sha256:8c6fcecd1183a7fc897548dfe388b0cdb7f37e018200d8409cf33959dbe35387", size = 182195, upload-time = "2026-05-04T23:06:09.72Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3f/e8/7325d258199b159eb2c03fe32107533e2832e70e63f4fb88a6aa00023201/pyopenssl-26.4.0.tar.gz", hash = "sha256:28dfcce0162b9211413e26dfbfdf1d24317fbeba18fc93c12400a1856b2a0bc7", size = 182046, upload-time = "2026-08-01T19:50:50.512Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/73/b8/a0e2790ae249d6f38c9f66de7a211621a7ab2650217bcd04e1262f578a56/pyopenssl-26.2.0-py3-none-any.whl", hash = "sha256:4f9d971bc5298b8bc1fab282803da04bf000c755d4ad9d99b52de2569ca19a70", size = 55823, upload-time = "2026-05-04T23:06:08.395Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/ad/2cf6d3fa2fae5c79e1ed9960c0d42badd0f94d81dd12b50604cdc839e648/pyopenssl-26.4.0-py3-none-any.whl", hash = "sha256:f0eb0cb2d581d3ad2b9c489468485e7f2ab6727d08401bcf9d824c3caddf3c1c", size = 56026, upload-time = "2026-08-01T19:50:48.94Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6049,11 +6052,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.5.5"
|
||||
version = "0.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5f/d3/3f06a1006f2261d1342aefb3c71eed02f5d4ca5bdbecd86ebc12ad38306e/sqlparse-0.6.0.tar.gz", hash = "sha256:113c35c75365ab9cc9c7231d68c6428fb11c085fc8e9eb1ad659b7ddbf6cd2b9", size = 178477, upload-time = "2026-08-13T19:16:06.396Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/50/f00935da0ec7cbf325f8dc4f772ae46fbc7b672dd62876e73f0a94adda57/sqlparse-0.6.0-py3-none-any.whl", hash = "sha256:b861c0288ce2fa56209a9a6412d2e066ac664b3873b89c26c9d8415e8e32996f", size = 50070, upload-time = "2026-08-13T19:16:04.062Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6117,6 +6120,21 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/9c/38a74d0f7a89b4320f6d2366fb660638bda8860daa08748b12c713d84381/stackit_resourcemanager-0.8.0-py3-none-any.whl", hash = "sha256:dd04bb8353d041a137c4dcba190beabded7acfaff1bc98b218fce20a99389ebc", size = 81288, upload-time = "2026-05-13T09:43:07.81Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stackit-ske"
|
||||
version = "1.12.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "python-dateutil" },
|
||||
{ name = "requests" },
|
||||
{ name = "stackit-core" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cd/9e/df3ad585cb96d028354f4253568e9879d81bb9395d5ebfa268fa9350e2df/stackit_ske-1.12.0.tar.gz", hash = "sha256:62814279f3b7fb2387648f92d14453a8905ad60115c07579f2741ddb7d1fcc94", size = 37239, upload-time = "2026-06-30T11:18:49.39Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/00/37/dc54fb7185a2d4da37308322ea1a7b992312030b2e37262de4eb4003f5c7/stackit_ske-1.12.0-py3-none-any.whl", hash = "sha256:45bd8084d87f14f818b3d7e824450248c8784ed204ca1b2dc108f491dcbdb1a3", size = 93142, upload-time = "2026-06-30T11:18:48.233Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "statsd"
|
||||
version = "4.0.1"
|
||||
@@ -6225,6 +6243,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "truststore"
|
||||
version = "0.10.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typer"
|
||||
version = "0.21.1"
|
||||
@@ -6622,40 +6649,70 @@ wheels = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "1.5.7.3"
|
||||
name = "zstandard"
|
||||
version = "0.25.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/49/62/b9c075ad664e7c4cbb3d8d2be7c246506abe1bc7f778eb58d260ef9538c8/zstd-1.5.7.3.tar.gz", hash = "sha256:403e5205f4ac04b92e6b0cda654be2f51de268228a0db0067bc087faacf2f495", size = 672559, upload-time = "2026-01-08T16:24:43.361Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/75/0d/8c89c0d010b58c21a7865a239790bb1c6822029c053b1ded858d6b573e3a/zstd-1.5.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a3c1781a24e2ced2c0ddee11d45b1f04018b03615eeb622a62eca4d56d3358a", size = 267641, upload-time = "2026-01-08T16:30:50.812Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/6d/155d8c344d96eca2a5a003a5ddd63373a5f13591fd5cf2b9490250d6805a/zstd-1.5.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a6c7c81056362b60a04baa34632e713d596662a860ec34efd8e9b109c10e6ec7", size = 230962, upload-time = "2026-01-08T16:30:49.155Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/c7/ab93916a26eb58cd501ad701974c31b4bc67a7f6abd6c24bef8fe4d7649b/zstd-1.5.7.3-cp311-cp311-manylinux_2_14_x86_64.whl", hash = "sha256:e564f34a55effc7d654eb293468edc80b64d476b0f899f82760ecd8323223ff5", size = 304166, upload-time = "2026-01-10T11:17:45.697Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/54/27a7040a360019a4602343e3c98c0c0a140f382186002c01e1992fd21837/zstd-1.5.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:fbc49a57188184931d5e3c9f1133cad7eea5a370a9e9418fb8122d58c14340a5", size = 1540288, upload-time = "2026-01-08T17:50:26.913Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/93/4a4d4edd1b2e809e0ebbb16000404bdcc9a09743c04ee1661442c9581b75/zstd-1.5.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:d121d3e63722819e1fe5effbcd9628d8a7cfea0cddabcc5bb37ea861a6a83424", size = 1619134, upload-time = "2026-01-08T17:50:32.324Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/31/6b/cd6f0a7f4f0d98e4110aa77763cf3e85f594d983ea9ca3d64cc0cee10684/zstd-1.5.7.3-cp311-cp311-manylinux_2_4_i686.whl", hash = "sha256:621f2e7ca8e9eb52a83eb9c91ec3cd283d87591bf75cc658de486b65f44742c7", size = 300166, upload-time = "2026-01-10T11:12:27.938Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/3f/c717e0d15127d04b7fa58ba9b4c56e8b88b803048b9766cd9d158dbb22ea/zstd-1.5.7.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:c1950fcae690ba32d0f31702b335c548fb42547821565925e48576afdad774a5", size = 1525776, upload-time = "2026-01-08T17:50:35.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/a2/1813cd787d1a2f9ab8e8a90d28dcbc8e8098997dd04de38897ea8e75dd08/zstd-1.5.7.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bac4f0d03da69115878bedbfa03c4a3f64364e8396b432028c4ce0f05141a0fb", size = 2096057, upload-time = "2026-01-08T17:50:33.984Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/ce/f5a3c7c12de458dd9ce15c484d627fe5412b60c155da23dacb5fcf08d9d5/zstd-1.5.7.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:da0ab134b7fd28023dedf013751ca850de300a090eb11f689d2a1c178c87d9dc", size = 2132659, upload-time = "2026-01-08T17:50:29.534Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/66/151f9546498bfd8971a0b6ad67d87c26d7a0df17d57f724da674f3778666/zstd-1.5.7.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b9923175842ee8f7602ec9cc578f5fc396896f0e8460d3ac9a5adc3cea77244e", size = 2124811, upload-time = "2026-01-08T17:50:37.612Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/34/4d2dbb36cb2373d3f115c047cb901b64f89de0703d10779da39de9453812/zstd-1.5.7.3-cp311-cp311-win32.whl", hash = "sha256:0612b604948d7b58aecc6788c7ceb53c5f21d94a155bb6ea9bd0f54ffa43725d", size = 150363, upload-time = "2026-01-08T17:11:02.392Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/de/f53687e0dd8c0d0ebfaed9ae88f6a96a1a0388ae7424b469e74bb17ac57d/zstd-1.5.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:5b7f8c81b2bd3b62c0345242247d484cafa4b518d59d18619813d9225af5c5c3", size = 167577, upload-time = "2026-01-08T17:11:03.356Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/58/d4a6a902e229e953ed273fe9b78587ed31f57567aa68d3e34af6056e42af/zstd-1.5.7.3-cp311-cp311-win_arm64.whl", hash = "sha256:ea112e3acd9e1765adca35df7b54ac75b36194290f64ea03a3a59664209c8527", size = 157238, upload-time = "2026-01-08T16:36:06.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/ed/5a3bf2e29dc56d4cc7619929bb51f0c758de6d02967cc73c5d8755a862c0/zstd-1.5.7.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:01a39efb0eeab7cc45cb308618233b624b0840d5e16dcf85456b6cca0592f203", size = 268124, upload-time = "2026-01-08T16:29:57.091Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/1d/efc2074ac90af938e78f2ed4004639fe24f294d9086c5280f8d9a02b9897/zstd-1.5.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7a8e8838cf35fa3987bfe1958584cc22e1797efce8e155a63544b4144fc671f8", size = 230988, upload-time = "2026-01-08T16:29:55.604Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/52/178393b8d70e23fba67f42dfce4663e4e8a30867110168beb490a36d4639/zstd-1.5.7.3-cp312-cp312-manylinux_2_14_i686.whl", hash = "sha256:f3920ac1d1cc7e9f252f3e29f217fe3cd36f2191bb3dbcae826c29e189b7ad54", size = 300207, upload-time = "2026-01-10T11:26:58.351Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/7a/8dcd86a2efb2ed3f9dae39545a05d3c7ed26c7678330786ce4a44cd8b099/zstd-1.5.7.3-cp312-cp312-manylinux_2_14_x86_64.whl", hash = "sha256:143f9062953fb5590cbd47c1040d357336742c79696bf90b6d5b835279a68304", size = 304154, upload-time = "2026-01-10T11:17:40.91Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/ce/0c96905ab01ffe0e53a3cec8132123b82db26bd583a71608029bcc789ebc/zstd-1.5.7.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36d1fd8647e47e1f21b345e192f1a279e925678c23dad8236b547d04456cd699", size = 2162222, upload-time = "2026-01-08T18:02:22.762Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/c4/db4807d6a68b4628c74fd379de7e3c67ec34f19a2a80ac246b3837cde6cb/zstd-1.5.7.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1538db419afa62773cf534fc7f3009ff59ecf55ecee4e889587ac2ef0010ed8", size = 2201732, upload-time = "2026-01-08T18:02:20.835Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/99/c19a3c0f5580ff9c33a74f06d98d6060ed1fa6bd09b55aed9be852ec191f/zstd-1.5.7.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c5efd16adb092e2a547a7d51cfdaf6fd5680528227684c5bafc7669ab4a55f41", size = 2096459, upload-time = "2026-01-08T18:02:25.336Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/fd/02eac30419475dbe50212c119043a2d0698a0cbc756da85fd3fd9abddf42/zstd-1.5.7.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:39b3438e64637d80a5b1860526903b92020acb9bae9ceb5adffd9838c1441328", size = 2125442, upload-time = "2026-01-08T18:02:17.715Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/43/3a16ff0a8c913bb9825379db1bd533c75c57c2d2f31dd9111aa9b53711f4/zstd-1.5.7.3-cp312-cp312-win32.whl", hash = "sha256:cbf48c53461e224ffc2490cfe5120a1ff40d14c84d2b512c6d6d99fc91685cf3", size = 150367, upload-time = "2026-01-08T17:03:40.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/83/b85875d7428e63dfa9247e41d17fac611443c774f7892f8643bd4164a6b2/zstd-1.5.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:943a189910f2fea997462e3e4d7fbf727a06d231ef801ebee557b1c87568981c", size = 167604, upload-time = "2026-01-08T17:03:41.355Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/42/cf291e26804de2f55500cdac93f5e9fa6267cf315def8aa402529bae3a87/zstd-1.5.7.3-cp312-cp312-win_arm64.whl", hash = "sha256:85c4d508f8109afa7c51c4960626c3325af2cf1e442c6c36ebfea15d04757e3f", size = 157241, upload-time = "2026-01-08T16:47:34.615Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/b8/d13d584867d5eb1bc607877a870858e02a256d4706a4274e475413a000aa/zstd-1.5.7.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:76c49ea969bc08389ea59155cea7c5dea224522ffc62f443f3c0a915f5fd184d", size = 260025, upload-time = "2026-01-08T16:57:45.739Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/a1/1e5faf75bedfd2bfccfb83e18736b115bed6e348504bd21800cd8f30dcea/zstd-1.5.7.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b1a638ff3dfce8f4cb1203c662fb5606dd99b4a62c5ddc4c406d2d1326bcfdd", size = 221038, upload-time = "2026-01-08T17:16:32.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/2c/0fe74d8b2029eef8000bc71aac5b3e5b55d00581238711cf627814183ea3/zstd-1.5.7.3-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5e96a5cb100a0edc162935227f2d9784b1031ce4a8a83e96e66eae2673c10143", size = 326792, upload-time = "2026-01-08T16:57:35.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/e0/2c7f081f3524f872128ff31bea2acb6b21cb1dacccef920eb6a1a77a87c6/zstd-1.5.7.3-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bda0bbf3a9553720cd33f1f85940a259656c7ffba4be717ff82b7f062052188", size = 322283, upload-time = "2026-01-08T16:57:36.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/a7/3bebfcc18d66b90bc7b506a61b2ff4af5ee1b0b16e784ea644afa06241c5/zstd-1.5.7.3-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac36e4022422f6e49b3f07bdbb8a964fd348223d3dc9c82ad5398a4f0432a719", size = 311553, upload-time = "2026-01-08T16:57:38.465Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/75/8a791cae2c98e5e44a158e15db50d21b7ec0b37aeaffa68d151bc8ffb6d6/zstd-1.5.7.3-pp311-pypy311_pp73-manylinux_2_14_x86_64.whl", hash = "sha256:fa4d760a220541b18ce732a3a2cf7547ea05afc76d05b3b39edebfeb721f6079", size = 317071, upload-time = "2026-01-08T16:36:07.47Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/25/b6624e6b08d515242154436c9d06fb20b790d300ac82e84f3c4c133e25e1/zstd-1.5.7.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a69e60146bf8aaa6a0e6c9a94a7c5f3133d68091e2e5c5a3c5ababf71fd5ec7a", size = 167654, upload-time = "2026-01-08T17:00:56.667Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "1.5.7.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0f/78/9a476e09c825304df47b98be80d1ffe223733b03550af71325415028f615/zstd-1.5.7.2.tar.gz", hash = "sha256:6d8684c69009be49e1b18ec251a5eb0d7e24f93624990a8a124a1da66a92fc8a", size = 670481, upload-time = "2025-06-23T12:36:08.131Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/43/2a/0885f6f1921ec1ef4a8f8ab29ab0a335cc867abe4c7aaa4e5031435a32a5/zstd-1.5.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f799c1e9900ad77e7a3d994b9b5146d7cfd1cbd1b61c3db53a697bf21ffcc57b", size = 269702, upload-time = "2025-06-23T12:50:11.695Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/e6/629cf6b77e47fc7149f5724fb4853c48edcdeb10d8c64e391d7026cb10e1/zstd-1.5.7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ff4c667f29101566a7b71f06bbd677a63192818396003354131f586383db042", size = 228145, upload-time = "2025-06-23T12:50:10.411Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/b8/9ddefd4670bfe9328ca6657ad335eb8d9c657466247e234a579818b6b0b9/zstd-1.5.7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8526a32fa9f67b07fd09e62474e345f8ca1daf3e37a41137643d45bd1bc90773", size = 1536530, upload-time = "2025-06-23T13:51:38.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/6a/1bb836c18760dc1e28ca7a9706016e482ebdea633b980d8505dbb65e18f8/zstd-1.5.7.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:2cec2472760d48a7a3445beaba509d3f7850e200fed65db15a1a66e315baec6a", size = 1616141, upload-time = "2025-06-23T13:51:34.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/7a/bb6c6e2cb2a066e347dc27d45d5205058b69d6c8b8d4ae2ee7d6b91c64a5/zstd-1.5.7.2-cp311-cp311-manylinux_2_4_i686.whl", hash = "sha256:a200c479ee1bb661bc45518e016a1fdc215a1d8f7e4bf6c7de0af254976cfdf6", size = 322188, upload-time = "2025-06-23T13:01:48.704Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/4f/cf0669c8a89fdcc91814bf92bd05cc363d5d12a79b656418c0add6f2d266/zstd-1.5.7.2-cp311-cp311-manylinux_2_4_x86_64.whl", hash = "sha256:f5d159e57a13147aa8293c0f14803a75e9039fd8afdf6cf1c8c2289fb4d2333a", size = 302736, upload-time = "2025-06-23T13:05:33.649Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/bc/e5f8b7f61826323e39e099db1eb5c0e09b18315df1b1ff778f7ae9aadcac/zstd-1.5.7.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:7206934a2bd390080e972a1fed5a897e184dfd71dbb54e978dc11c6b295e1806", size = 1522687, upload-time = "2025-06-23T13:51:35.494Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/8c/7660a949a020ac9d02b3166a25dd1c12144572d77b11ae92a31d341016da/zstd-1.5.7.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e0027b20f296d1c9a8e85b8436834cf46560240a29d623aa8eaa8911832eb58", size = 2098794, upload-time = "2025-06-23T13:51:37.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/b2/730c811a78d670104d40c7f08cc8092577cdff870cba42b3158f20fceb57/zstd-1.5.7.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d6b17e5581dd1a13437079bd62838d2635db8eb8aca9c0e9251faa5d4d40a6d7", size = 2112266, upload-time = "2025-06-23T13:51:31.258Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/74/2c16e1632094db36c8920d4c13b8e2e843024d548ae26888c2d22af6a676/zstd-1.5.7.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b13285c99cc710f60dd270785ec75233018870a1831f5655d862745470a0ca29", size = 2109465, upload-time = "2025-06-23T13:51:32.884Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/6e/b9c9a834769d96cab2122da1be8c8c700d3f76be796d2b7516e85d2eca0e/zstd-1.5.7.2-cp311-cp311-win32.whl", hash = "sha256:cdb5ec80da299f63f8aeccec0bff3247e96252d4c8442876363ff1b438d8049b", size = 149448, upload-time = "2025-06-23T13:06:21.144Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/b7/fc22ad6292a32d7676ab815de3a23573beac3679e8abd9914288d1496ceb/zstd-1.5.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:4f6861c8edceb25fda37cdaf422fc5f15dcc88ced37c6a5b3c9011eda51aa218", size = 166592, upload-time = "2025-06-23T13:06:22.126Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/14/096bb77f3e5ef525b452cd6294da33de7f8a8c9647ba78293378fbb0a7ce/zstd-1.5.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ebe3e60dbace52525fa7aa604479e231dc3e4fcc76d0b4c54d8abce5e58734", size = 269408, upload-time = "2025-06-23T13:11:46.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/b8/2bc2590a34c733ea0570f366e6ad7d889d05c7825bd3ccab01f36ece71c6/zstd-1.5.7.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ef201b6f7d3a6751d85cc52f9e6198d4d870e83d490172016b64a6dd654a9583", size = 228188, upload-time = "2025-06-23T13:11:47.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/80/6252de3a70cfd7767718ad476893f1c7dc129f942cc7ed0322e3137c03d9/zstd-1.5.7.2-cp312-cp312-manylinux_2_14_x86_64.whl", hash = "sha256:ac7bdfedda51b1fcdcf0ab69267d01256fc97ddf666ce894fde0fae9f3630eac", size = 302720, upload-time = "2025-06-23T12:40:11.522Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/b6/af908387814b99172d3aea6aeb24b19583aadfa45f6021e5e2a0d6d8e99a/zstd-1.5.7.2-cp312-cp312-manylinux_2_4_i686.whl", hash = "sha256:b835405cc4080b378e45029f2fe500e408d1eaedfba7dd7402aba27af16955f9", size = 322237, upload-time = "2025-06-23T13:17:35.482Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/d7/ab9142e002a7eaa451cb4bb37a74c390c489ba8ae75ade543840496eda04/zstd-1.5.7.2-cp312-cp312-win32.whl", hash = "sha256:e4cf97bb97ed6dbb62d139d68fd42fa1af51fd26fd178c501f7b62040e897c50", size = 149453, upload-time = "2025-06-23T13:13:02.786Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/c7/c182ea7bc283f591e3f3c5f0f239e7a92c9bc1f626642ae2c4dfbe51d6f2/zstd-1.5.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:55e2edc4560a5cf8ee9908595e90a15b1f47536ea9aad4b2889f0e6165890a38", size = 166628, upload-time = "2025-06-23T13:13:03.745Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/c9/a6495a7bf168a78f0a0c01d61d830ebfb401315a64fd1ae8d725c458114c/zstd-1.5.7.2-pp311-pypy311_pp73-manylinux_2_14_x86_64.whl", hash = "sha256:5fb2ff5718fe89181223c23ce7308bd0b4a427239379e2566294da805d8df68a", size = 315542, upload-time = "2025-06-23T12:39:27.598Z" },
|
||||
]
|
||||
|
||||
@@ -4,6 +4,117 @@ description: "New features and improvements in each Prowler release"
|
||||
rss: true
|
||||
---
|
||||
|
||||
<Update label="v5.39.0" description="August 13, 2026">
|
||||
### 🤖 Lighthouse AI — Finding Skills
|
||||
|
||||
<Note>
|
||||
This feature is available exclusively in **Prowler Cloud** and **Prowler Private Cloud** with a [subscription](https://prowler.com/pricing).
|
||||
</Note>
|
||||
|
||||
Lighthouse AI now embeds a Skills menu on every finding, answering the questions an analyst actually asks. **Contextual Fix** produces the fix for the finding, **Triage Decision** judges whether it is real and closes it out when it is not, and **Systemic Scope** determines whether the problem is a one-off or everywhere. A free-form "Ask Lighthouse anything" prompt sits in the same menu, and each run shows its progress and offers follow-up actions such as creating a Jira issue or muting the finding.
|
||||
|
||||

|
||||
|
||||
Read more in the [Lighthouse AI documentation](https://docs.prowler.com/getting-started/products/prowler-cloud-lighthouse).
|
||||
|
||||
### ☁️ Azure Management Group Onboarding
|
||||
|
||||
<Note>
|
||||
This feature is available exclusively in **Prowler Cloud** and **Prowler Private Cloud** with a [subscription](https://prowler.com/pricing).
|
||||
</Note>
|
||||
|
||||
Azure subscriptions no longer onboard one at a time. Choose "Add Multiple Subscriptions With Azure Management Group" in the add-provider wizard, enter the Microsoft Entra tenant ID, and authenticate once with a single tenant-wide service principal: Prowler discovers the entire management-group hierarchy under the tenant root, lets you select the subscriptions to onboard, and creates their providers with the management-group structure preserved. Azure now matches the one-step onboarding that AWS Organizations and GCP organizations already have.
|
||||
|
||||

|
||||
|
||||
Read more in the [Azure Management Groups documentation](https://docs.prowler.com/user-guide/tutorials/prowler-cloud-azure-management-groups).
|
||||
|
||||
### ✅ Findings Triage — Verify MANUAL Findings as PASS
|
||||
|
||||
<Note>
|
||||
This feature is available exclusively in **Prowler Cloud** and **Prowler Private Cloud** with a [subscription](https://prowler.com/pricing).
|
||||
</Note>
|
||||
|
||||
Checks that require human judgment report `MANUAL` findings. For these findings, and only for them, the triage status selector now offers **Resolved**: choosing it asks for the required written evidence and verifies the finding as passing. The finding then reports an effective `PASS` while preserving the raw `MANUAL` scan result, across findings, finding groups, compliance reports, and scans, with the attestation's author, evidence, and validity always visible. Attestations expire automatically after 90 days, or as soon as a new scan reports a real failure, returning the finding to the review queue.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Read more in the [Findings Triage documentation](https://docs.prowler.com/user-guide/tutorials/prowler-app-findings-triage#verify-a-manual-finding-as-pass).
|
||||
|
||||
### ☁️ Prowler Cloud MCP — Organizations Management and Grouped Jira Dispatch
|
||||
|
||||
<Note>
|
||||
This feature is available exclusively in **Prowler Cloud** and **Prowler Private Cloud** with a [subscription](https://prowler.com/pricing).
|
||||
</Note>
|
||||
|
||||
The hosted Prowler Cloud MCP server adds eight organization tools, so an agent can onboard and manage entire cloud organizations end to end: create the organization, discover its accounts, subscriptions, and projects, apply the selection, and manage the resulting providers. The tools cover AWS Organizations, GCP organizations, and Azure tenant root management groups, and they are available to Lighthouse AI.
|
||||
|
||||
`prowler_send_findings_to_jira` also gains Cloud-only dispatch capabilities: select failed findings by check IDs against the latest completed scan, and send them in grouped mode, one Jira work item per check listing up to 50 affected resources, with per-group error reporting.
|
||||
|
||||
Read more in the [Prowler MCP tools documentation](https://docs.prowler.com/getting-started/basic-usage/prowler-mcp-tools) and its [Jira operations reference](https://docs.prowler.com/getting-started/basic-usage/prowler-mcp-tools#jira-operations).
|
||||
|
||||
### 🕸️ Attack Paths — Grouped Graph with Outcome Destinations
|
||||
|
||||
<Note>
|
||||
This feature is available exclusively in **Prowler Cloud** and **Prowler Private Cloud** with a [subscription](https://prowler.com/pricing).
|
||||
</Note>
|
||||
|
||||
The Attack Paths graph now reads from source to destination. Resources of the same class collapse into a single expandable node with a count, clicking reveals its members, and every path terminates in an explicit outcome node naming the destination impact: code execution, privilege escalation, public exposure, or resource inventory. The per-account hub node is gone, and the clicked resource stays highlighted while its findings are expanded.
|
||||
|
||||

|
||||
|
||||
Explore the full Attack Paths query catalog at [Prowler Hub](https://hub.prowler.com/attack-paths).
|
||||
|
||||
Read more in the [Attack Paths documentation](https://docs.prowler.com/user-guide/tutorials/prowler-app-attack-paths).
|
||||
|
||||
### 📚 New Compliance Framework — CMMC 2.0
|
||||
|
||||
The Cybersecurity Maturity Model Certification (CMMC) is the certification the US Department of Defense requires from contractors and suppliers that handle federal contract data. Prowler now includes CMMC 2.0 as a universal framework with all 149 requirements defined by the CMMC Program rule (32 CFR Part 170), organized in its three levels:
|
||||
|
||||
- **Level 1 (Foundational):** 15 requirements for the basic safeguarding of Federal Contract Information, from FAR 52.204-21.
|
||||
- **Level 2 (Advanced):** 110 requirements from NIST SP 800-171 Rev 2, protecting Controlled Unclassified Information.
|
||||
- **Level 3 (Expert):** 24 enhanced requirements from NIST SP 800-172 for the most sensitive programs.
|
||||
|
||||
Requirements map to Prowler checks across AWS, Azure, GCP, Alibaba Cloud, Oracle Cloud, and Microsoft 365, so one framework reports the compliance posture of the whole estate.
|
||||
|
||||
Read more in the [Compliance documentation](https://docs.prowler.com/user-guide/compliance/tutorials/compliance).
|
||||
|
||||
### 🔍 Checks
|
||||
|
||||
#### Microsoft 365
|
||||
|
||||
Twenty new Entra ID checks expand the coverage of CIS Microsoft 365 Foundations Benchmark v7.0.0:
|
||||
|
||||
- **Password protection:** custom banned password list, on-premises enforcement, and lockout threshold and duration.
|
||||
- **Default user permissions:** security group and Microsoft 365 group creation restricted, and guest invitations limited to allowed domains.
|
||||
- **Conditional Access:** high and medium sign-in risk blocked, authentication transfer blocked, untrusted locations blocked, trusted named locations defined, sign-in frequency enforced, and token protection enforced.
|
||||
- **Sessions and authentication methods:** idle session timeout configured, email one-time passcodes disabled, and Microsoft Authenticator context shown.
|
||||
- **PIM and access reviews:** approval required to activate the Global Administrator and Privileged Role Administrator roles, and access reviews configured for guest users and privileged roles.
|
||||
|
||||
Explore all Microsoft 365 checks at [Prowler Hub](https://hub.prowler.com/check?provider=m365).
|
||||
|
||||
#### AWS
|
||||
|
||||
Two new checks detect hardcoded secrets:
|
||||
|
||||
- `batch_job_definition_no_secrets` scans Batch job definition environment variables and command parameters. Thanks to @praneetrajv!
|
||||
- `awslambda_layer_no_secrets_in_content` scans Lambda layer package content. Thanks to @ganiganesh25!
|
||||
|
||||
Explore all AWS checks at [Prowler Hub](https://hub.prowler.com/check?provider=aws).
|
||||
|
||||
### 🙌 External Contributors
|
||||
|
||||
Thank you to our community contributors for this release!
|
||||
|
||||
- @praneetrajv: AWS `batch_job_definition_no_secrets` check ([#12117](https://github.com/prowler-cloud/prowler/pull/12117))
|
||||
- @ganiganesh25: AWS `awslambda_layer_no_secrets_in_content` check ([#12233](https://github.com/prowler-cloud/prowler/pull/12233))
|
||||
- @andoniaf: GitHub `organization_repository_creation_limited` now reports low severity when repository creation is limited to private or internal visibility ([#12164](https://github.com/prowler-cloud/prowler/pull/12164))
|
||||
|
||||
See the [full release notes on GitHub](https://github.com/prowler-cloud/prowler/releases/tag/5.39.0) for the complete list of changes.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.38.0" description="August 6, 2026">
|
||||
### 📌 Compliance Watchlist
|
||||
|
||||
|
||||
@@ -224,9 +224,9 @@ Each check **must** populate the report with a unique identifier for the audited
|
||||
- `resource_name`: Description of the configuration (e.g., "SharePoint Settings")
|
||||
- GitHub
|
||||
- Resource ID — `report.resource_id`.
|
||||
- The ID of the Github resource. This is a system-generated integer that uniquely identifies the resource within the Github platform.
|
||||
- The ID of the GitHub resource. This is a system-generated integer that uniquely identifies the resource within the GitHub platform.
|
||||
- Resource Name — `report.resource_name`.
|
||||
- The name of the Github resource. In the case of a repository, this is just the repository name. For full repository names use the resource `full_name`.
|
||||
- The name of the GitHub resource. In the case of a repository, this is just the repository name. For full repository names use the resource `full_name`.
|
||||
|
||||
### Configurable Checks in Prowler
|
||||
|
||||
|
||||
@@ -120,14 +120,14 @@ class NewFeatureTools(BaseTool):
|
||||
|
||||
Returns complete feature details including configuration and metadata.
|
||||
"""
|
||||
try:
|
||||
response = await self.api_client.get(f"/api/v1/features/{feature_id}")
|
||||
return DetailedFeature.from_api_response(response["data"]).model_dump()
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to get feature {feature_id}: {e}")
|
||||
return {"error": str(e), "status": "failed"}
|
||||
response = await self.api_client.get(f"/api/v1/features/{feature_id}")
|
||||
return DetailedFeature.from_api_response(response["data"]).model_dump()
|
||||
```
|
||||
|
||||
There is no `try`/`except` here on purpose. A failed request raises, and
|
||||
[Error Handling](#error-handling) explains what turns that raise into a message
|
||||
the agent can act on.
|
||||
|
||||
### Step 2: Create the Models
|
||||
|
||||
Create corresponding models in `prowler_app/models/`:
|
||||
@@ -369,18 +369,62 @@ async def search_items(self, status: str = Field(...)) -> dict:
|
||||
|
||||
### Error Handling
|
||||
|
||||
Return structured error responses instead of raising exceptions:
|
||||
**Raise, never return.** A returned `{"error": ...}` dict is reported to the
|
||||
client as `isError: false` -- a *successful* tool call whose payload happens to
|
||||
mention a failure. Clients and models read that as success. A raised exception
|
||||
becomes a spec-correct tool execution error instead.
|
||||
|
||||
The common case therefore needs no handler at all:
|
||||
|
||||
```python
|
||||
async def get_item(self, item_id: str) -> dict:
|
||||
try:
|
||||
response = await self.api_client.get(f"/api/v1/items/{item_id}")
|
||||
return DetailedItem.from_api_response(response["data"]).model_dump()
|
||||
except Exception as e:
|
||||
self.logger.error(f"Failed to get item {item_id}: {e}")
|
||||
return {"error": str(e), "status": "failed"}
|
||||
response = await self.api_client.get(f"/api/v1/items/{item_id}")
|
||||
return DetailedItem.from_api_response(response["data"]).model_dump()
|
||||
```
|
||||
|
||||
`prowler_mcp_server/lib/errors.py` classifies the failures every tool shares --
|
||||
a rejected credential, a missing permission, a rate limit, an outage, an
|
||||
unreachable API, a bad argument -- and gives each one a message that says what
|
||||
went wrong and what to do about it. Anything it does not recognise is masked,
|
||||
because `mask_error_details=True` is set on every sub-server and upstream
|
||||
response bodies must never be replayed into a model's context.
|
||||
|
||||
Three ways to raise, in the order to reach for them:
|
||||
|
||||
```python
|
||||
from fastmcp.exceptions import ToolError
|
||||
|
||||
from prowler_mcp_server.lib.errors import InvalidArgument
|
||||
|
||||
# 1. An argument this server rejected before any request went out. The message
|
||||
# is repeated to the agent verbatim, so write it for one to read.
|
||||
if not 1 <= page_size <= 1000:
|
||||
raise InvalidArgument("page_size must be between 1 and 1000.")
|
||||
|
||||
# 2. A request the API answered or never answered: let it propagate untouched.
|
||||
# `ProwlerAPIError` and `ProwlerAPIUnreachable` are what the classifier keys
|
||||
# on, and the second one is what stops a retry from duplicating a write.
|
||||
response = await self.api_client.get(f"/api/v1/items/{item_id}")
|
||||
data = response["data"]
|
||||
|
||||
# 3. A sentence the classifier cannot know -- a resource name, a precondition,
|
||||
# the next tool to call. NOTE the absent `from` clause: it is what marks the
|
||||
# message as already final. With `from e` the classifier would replace it.
|
||||
if not data:
|
||||
raise ToolError(
|
||||
f"No item with the ID {item_id!r} exists. Use prowler_list_items to "
|
||||
"find a valid one."
|
||||
)
|
||||
```
|
||||
|
||||
The one thing that still *returns* rather than raises is a write whose outcome is
|
||||
genuinely unknown. `prowler_send_findings_to_jira` is the worked example: work
|
||||
items are created one at a time and Prowler cannot delete them, so a dispatch
|
||||
that stopped halfway answers with a result object carrying
|
||||
`safe_to_retry: false`. "This may have been applied" is a fact about the world,
|
||||
not an error, and squashing it into one loses the only thing that stops a retry
|
||||
from duplicating the write.
|
||||
|
||||
### Parameter Descriptions
|
||||
|
||||
Use Pydantic `Field()` with clear descriptions. This also helps LLMs understand
|
||||
|
||||
@@ -107,7 +107,7 @@ Once you have decided the provider you want or need to add to Prowler, the next
|
||||
- **SDK Providers**: Low complexity. You have mature examples like AWS, Azure, GCP, Kubernetes, etc. that you can leverage to implement your provider.
|
||||
- **API Providers**: Medium complexity. You need to implement the authentication and session management, and the API calls to the provider. You now have NHN and MongoDB Atlas as example to follow.
|
||||
- **Tool/Wrapper Providers**: High complexity. You need to implement the argument/output mapping to the provider and handle problems that the tool/wrapper may have. You now have IAC and the PowerShell wrapper as example to follow.
|
||||
- **Hybrid Providers**: High complexity. You need to "customize" your provider, mixing the other types of providers in order to achieve the desired result. You have M365 (msgraph SDK + PowerShell wrapper) and Github (PyGithub SDK + graphql API requests) as examples.
|
||||
- **Hybrid Providers**: High complexity. You need to "customize" your provider, mixing the other types of providers to achieve the desired result. You have M365 (msgraph SDK + PowerShell wrapper) and GitHub (PyGithub SDK + graphql API requests) as examples.
|
||||
|
||||
### Determining Regional vs Non-Regional Architecture
|
||||
|
||||
@@ -814,7 +814,7 @@ class YourProviderMutelist(Mutelist):
|
||||
Region management is essential for cloud providers that operate across multiple geographic locations. This component handles region validation and provides region-specific functionality.
|
||||
|
||||
<Note>
|
||||
Regions are optional, only if the provider has regions, for example Github does not have regions, but AWS does.
|
||||
Regions are optional, only if the provider has regions, for example GitHub does not have regions, but AWS does.
|
||||
</Note>
|
||||
|
||||
**File:** `prowler/providers/<provider_name>/lib/regions/<provider_name>_regions.py`
|
||||
@@ -1773,7 +1773,7 @@ The implementation of the mutelist is the same as the [SDK providers](#step-5-im
|
||||
Region management is essential for cloud providers that operate across multiple geographic locations. This component handles region validation and provides region-specific functionality.
|
||||
|
||||
<Note>
|
||||
Regions are optional, only if the provider has regions, for example Github does not have regions, but AWS does.
|
||||
Regions are optional, only if the provider has regions, for example GitHub does not have regions, but AWS does.
|
||||
</Note>
|
||||
|
||||
**File:** `prowler/providers/<provider_name>/lib/regions/<provider_name>_regions.py`
|
||||
|
||||
@@ -525,7 +525,7 @@ with mock.patch(
|
||||
):
|
||||
```
|
||||
|
||||
As demonstrated in the code above, mocking both the AWS audit information and all utilized services is mandatory for proper test execution.
|
||||
As demonstrated in the code above, mocking both the AWS audit information and all used services is mandatory for proper test execution.
|
||||
|
||||
#### Patching vs. Importing
|
||||
|
||||
|
||||
@@ -182,7 +182,8 @@
|
||||
"pages": [
|
||||
"user-guide/tutorials/prowler-app-s3-integration",
|
||||
"user-guide/tutorials/prowler-app-security-hub-integration",
|
||||
"user-guide/tutorials/prowler-app-jira-integration"
|
||||
"user-guide/tutorials/prowler-app-jira-integration",
|
||||
"user-guide/tutorials/prowler-app-slack-integration"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -301,6 +302,15 @@
|
||||
{
|
||||
"group": "Providers",
|
||||
"pages": [
|
||||
{
|
||||
"group": "Organizations",
|
||||
"pages": [
|
||||
"user-guide/organizations",
|
||||
"user-guide/providers/aws/organizations",
|
||||
"user-guide/providers/gcp/organization",
|
||||
"user-guide/providers/azure/management-groups"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Alibaba Cloud",
|
||||
"pages": [
|
||||
@@ -330,6 +340,7 @@
|
||||
"user-guide/providers/azure/getting-started-azure",
|
||||
"user-guide/providers/azure/authentication",
|
||||
"user-guide/providers/azure/use-non-default-cloud",
|
||||
"user-guide/providers/azure/management-groups",
|
||||
"user-guide/providers/azure/subscriptions",
|
||||
"user-guide/providers/azure/resource-groups",
|
||||
"user-guide/providers/azure/create-prowler-service-principal"
|
||||
|
||||
@@ -184,7 +184,7 @@ Prowler enables security scanning of Kubernetes clusters, supporting both **in-c
|
||||
```
|
||||
|
||||
<Note>
|
||||
By default, Prowler scans all namespaces in the active Kubernetes context. Use the `--context`flag to specify the context to be scanned and `--namespaces` to restrict scanning to specific namespaces.
|
||||
By default, Prowler scans all namespaces in the active Kubernetes context. Use the `--context` flag to specify the context to be scanned and `--namespaces` to restrict scanning to specific namespaces.
|
||||
|
||||
</Note>
|
||||
## Microsoft 365
|
||||
|
||||
@@ -11,10 +11,10 @@ Complete reference guide for all tools available in the Prowler MCP Server. Tool
|
||||
| Prowler Hub | 10 tools | No | Cloud and Local MCP Server |
|
||||
| Prowler Documentation | 2 tools | No | Cloud and Local MCP Server |
|
||||
| Prowler Cloud, Private Cloud & Local Server | 49 tools | Yes | Cloud and Local MCP Server |
|
||||
| Prowler Cloud management | 32 tools | Yes | Cloud MCP Server only |
|
||||
| Prowler Cloud management | 40 tools | Yes | Cloud MCP Server only |
|
||||
|
||||
<Note>
|
||||
48 of the 49 Prowler tools are available on both servers. `prowler_schedule_daily_scan` is the exception: it is Local-only, because the Cloud MCP Server supersedes it with the `prowler_cloud_*` [Scan Scheduling](#scan-scheduling) tools.
|
||||
48 of the 49 Prowler tools are available on both servers. `prowler_schedule_daily_scan` is the exception: it is Local-only, because the Cloud MCP Server supersedes it with the `prowler_cloud_*` [Scan Scheduling](#scan-scheduling) tools. `prowler_send_findings_to_jira` is exposed by both servers but accepts two [extra parameters](#jira-operations) on the Cloud MCP Server.
|
||||
</Note>
|
||||
|
||||
## Tool Naming Convention
|
||||
@@ -124,7 +124,20 @@ Tools for managing where Prowler sends its results: Amazon S3 buckets, AWS Secur
|
||||
#### Jira Operations
|
||||
|
||||
- **`prowler_get_jira_issue_types`** - List the issue types available in a Jira project, fetched live from Jira
|
||||
- **`prowler_send_findings_to_jira`** - Create one Jira work item per finding, with its severity, resource, risk, and remediation steps
|
||||
- **`prowler_send_findings_to_jira`** - Create Jira work items from findings, each carrying the check title, severity, status, provider, region, resource, risk, and remediation steps. Select the findings either by ID with `finding_ids`, or — on Prowler Cloud only — by check with `check_ids`, and choose between one work item per finding or one per check with `dispatch_mode`
|
||||
|
||||
<Note>
|
||||
`check_ids` and `dispatch_mode` are **Prowler Cloud only**:
|
||||
|
||||
- **`check_ids`** - Send the failing findings of a check (for example `s3_bucket_public_access`) without listing their IDs. Prowler resolves them server-side, taking only the failed findings of the latest completed scan of every provider. Get the check IDs from `prowler_list_finding_groups`. Exactly one of `finding_ids` or `check_ids` is required — Prowler combines both filters, so sending both would only dispatch their intersection. A Local MCP Server rejects `check_ids` with a client error.
|
||||
- **`dispatch_mode`** - `individual` (the default) creates one work item per finding. `grouped` creates one work item per check instead, listing up to 50 affected resources and linking back to the finding group in Prowler Cloud, which keeps a noisy check to a single ticket. Grouped dispatch only covers failed, unmuted findings of the latest completed scan of every provider. A Local MCP Server ignores `dispatch_mode` instead of rejecting it, and creates one work item per finding.
|
||||
|
||||
In `grouped` mode the response counters change meaning: `created_count` counts work items (one per check) rather than findings, `failed_count` counts the entries of the new `failed_groups` field, and `failed_groups` details each failure with its reason and the `check_id` whose work item could not be created.
|
||||
</Note>
|
||||
|
||||
<Warning>
|
||||
`prowler_send_findings_to_jira` creates real work items that Prowler cannot delete or update afterwards. Only retry the same dispatch when the previous response returned `safe_to_retry: true`, otherwise the work items already created are duplicated. Combining `check_ids` with the default `individual` mode opens one work item per failing resource, which can be hundreds of them — use `dispatch_mode="grouped"` to keep it to one per check.
|
||||
</Warning>
|
||||
|
||||
### Attack Paths Analysis
|
||||
|
||||
@@ -167,6 +180,23 @@ Manage Prowler Cloud-only features and configuration. **Requires authentication.
|
||||
These tools are available **only on the Cloud MCP Server** (`https://mcp.prowler.com/mcp`). A Local MCP Server does not expose them, because the features they manage exist only in Prowler Cloud.
|
||||
</Note>
|
||||
|
||||
### Organizations
|
||||
|
||||
Tools for onboarding a cloud provider organization as a whole — an AWS Organization, an Azure tenant with its management groups, or a GCP organization with its folders. An organization holds org-level credentials, discovers the real account, subscription, or project structure in the cloud, and turns a selection from that discovery into Prowler providers linked into a hierarchy of nodes. Every tool that changes something — creating, updating, deleting, discovering, applying a discovery, or adjusting provider membership — requires the **Manage Providers** permission; listing and reading do not.
|
||||
|
||||
<Note>
|
||||
Use these tools for the whole organization. To register providers one by one, use the [Provider Management](#provider-management) tools instead; to build arbitrary RBAC buckets of providers, use provider groups.
|
||||
</Note>
|
||||
|
||||
- **`prowler_cloud_list_organizations`** - Browse the registered organizations with lightweight data (name, type, external id, provider and node counts), filtered by type or cloud-side external id
|
||||
- **`prowler_cloud_get_organization`** - Get one organization in full: attributes, linked providers, credentials status, latest discovery, and the OU / management group / folder hierarchy. Set `include_hierarchy` to `false` to skip the tree on large organizations
|
||||
- **`prowler_cloud_create_organization`** - Register an organization, optionally storing its org-level credentials in the same call. Idempotent: an organization with the same type and external id is reused and its credentials rotated, reported as `created: false`
|
||||
- **`prowler_cloud_update_organization`** - Rename an organization, replace its metadata, and/or create or rotate its org-level credentials. `org_type` and `external_id` are immutable after creation
|
||||
- **`prowler_cloud_delete_organization`** - Delete an organization, its entire hierarchy, and every linked provider
|
||||
- **`prowler_cloud_discover_organization`** - Enumerate the real cloud structure: AWS accounts and OUs, Azure subscriptions and management groups, or GCP projects and folders. Each item comes back with its registration state so you can choose what to onboard
|
||||
- **`prowler_cloud_apply_organization_discovery`** - Turn a discovery selection into Prowler providers and hierarchy nodes
|
||||
- **`prowler_cloud_manage_organization_providers`** - Manually `add`, `replace`, or `remove` the providers linked to an organization or to one of its hierarchy nodes. Providers are detached, never deleted
|
||||
|
||||
### Scan Configurations
|
||||
|
||||
Tools for managing reusable scan configurations — per-provider check and compliance selections — and attaching them to providers. Providers without a configuration attached use the default.
|
||||
|
||||
@@ -22,7 +22,7 @@ Google Cloud Security Command Center (Cloud SCC) is a centralized security and r
|
||||
|
||||
- **GCP-Centric:** While Cloud SCC is powerful within the GCP ecosystem, it is primarily focused on GCP and does not natively extend to multi-cloud environments without additional tools or connectors.
|
||||
|
||||
- **Cost Considerations:** As a managed service within GCP, costs can scale with the amount of data ingested and the complexity of the environment, especially as additional features or higher volumes of data are utilized.
|
||||
- **Cost Considerations:** As a managed service within GCP, costs can scale with the amount of data ingested and the complexity of the environment, especially as additional features or higher volumes of data are used.
|
||||
|
||||
- **Dependency on GCP Services:** Cloud SCC's capabilities depend on other GCP services being enabled, such as Security Health Analytics and Web Security Scanner, which may increase overall complexity and cost.
|
||||
|
||||
|
||||
@@ -128,12 +128,12 @@ To update the environment file:
|
||||
Edit the `.env` file and change version values:
|
||||
|
||||
```env
|
||||
PROWLER_UI_VERSION="5.38.0"
|
||||
PROWLER_API_VERSION="5.38.0"
|
||||
PROWLER_UI_VERSION="5.39.0"
|
||||
PROWLER_API_VERSION="5.39.0"
|
||||
```
|
||||
|
||||
<Note>
|
||||
You can find the latest versions of Prowler Local Server in the [Releases Github section](https://github.com/prowler-cloud/prowler/releases) or in the [Container Versions](#container-versions) section of this documentation.
|
||||
You can find the latest versions of Prowler Local Server in the [Releases GitHub section](https://github.com/prowler-cloud/prowler/releases) or in the [Container Versions](#container-versions) section of this documentation.
|
||||
</Note>
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ To install Prowler as a Python package, use `Python >= 3.10, <= 3.13`. Prowler i
|
||||
_Requirements_:
|
||||
|
||||
* Have `docker` installed: https://docs.docker.com/get-docker/.
|
||||
* In the command below, change `-v` to your local directory path in order to access the reports.
|
||||
* In the command below, change `-v` to your local directory path to access the reports.
|
||||
* AWS, GCP, Azure and/or Kubernetes credentials
|
||||
|
||||
_Commands_:
|
||||
|
||||
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 563 KiB |
|
After Width: | Height: | Size: 401 KiB |
|
After Width: | Height: | Size: 374 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 203 KiB |
|
After Width: | Height: | Size: 215 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 199 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 194 KiB |
|
After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 193 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 185 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 286 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 252 KiB |
|
After Width: | Height: | Size: 366 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 401 KiB |
|
After Width: | Height: | Size: 456 KiB |
|
After Width: | Height: | Size: 374 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 93 KiB |
@@ -92,6 +92,7 @@ li[id="/user-guide/tutorials/prowler-alerts"] a > div > div > span:first-child::
|
||||
li[id="/user-guide/tutorials/prowler-app-attack-paths-active-queries"] a > div > div > span:first-child::after,
|
||||
li[id="/user-guide/tutorials/prowler-app-findings-triage"] a > div > div > span:first-child::after,
|
||||
li[id="/user-guide/tutorials/prowler-app-scan-configuration"] a > div > div > span:first-child::after,
|
||||
li[id="/user-guide/tutorials/prowler-app-slack-integration"] a > div > div > span:first-child::after,
|
||||
li[id="/user-guide/tutorials/prowler-cloud-aws-organizations"] a > div > div > span:first-child::after,
|
||||
li[id="/user-guide/tutorials/prowler-cloud-azure-management-groups"] a > div > div > span:first-child::after,
|
||||
li[id="/user-guide/tutorials/prowler-cloud-gcp-organizations"] a > div > div > span:first-child::after,
|
||||
|
||||
@@ -495,7 +495,7 @@ aws:
|
||||
|
||||
# AWS CloudTrail Configuration
|
||||
# aws.cloudtrail_threat_detection_privilege_escalation
|
||||
threat_detection_privilege_escalation_threshold: 0.2 # Percentage of actions found to decide if it is an privilege_escalation attack event, by default is 0.2 (20%)
|
||||
threat_detection_privilege_escalation_threshold: 0.2 # Percentage of actions found to decide if it is a privilege_escalation attack event, by default is 0.2 (20%)
|
||||
threat_detection_privilege_escalation_minutes: 1440 # Past minutes to search from now for privilege_escalation attacks, by default is 1440 minutes (24 hours)
|
||||
threat_detection_privilege_escalation_actions:
|
||||
[
|
||||
|
||||
@@ -4,13 +4,13 @@ title: "Custom Checks Metadata"
|
||||
|
||||
In certain organizations, the severity of specific checks might differ from the default values defined in the check's metadata. For instance, while `s3_bucket_level_public_access_block` could be deemed `critical` for some organizations, others might assign a different severity level to it.
|
||||
|
||||
The custom metadata option offers a means to override default metadata set by Prowler
|
||||
The custom metadata option offers a means to override default metadata set by Prowler.
|
||||
|
||||
You can utilize `--custom-checks-metadata-file` followed by the path to your custom checks metadata YAML file.
|
||||
You can use `--custom-checks-metadata-file` followed by the path to your custom checks metadata YAML file.
|
||||
|
||||
## Available Fields
|
||||
|
||||
The list of supported check's metadata fields that can be override are listed as follows:
|
||||
The list of supported check's metadata fields that can be overridden are listed as follows:
|
||||
|
||||
- Severity
|
||||
- CheckTitle
|
||||
|
||||
@@ -99,7 +99,7 @@ def get_table(data):
|
||||
|
||||
## S3 Integration
|
||||
|
||||
If you are using Prowler Cloud with the S3 integration or that integration from Prowler CLI and you want to use your data from your S3 bucket, you can run the following command in order to load the dashboard with the new files:
|
||||
If you are using Prowler Cloud with the S3 integration or that integration from Prowler CLI and you want to use your data from your S3 bucket, you can run the following command to load the dashboard with the new files:
|
||||
|
||||
```sh
|
||||
aws s3 cp s3://<your-bucket>/output/csv ./output --recursive
|
||||
|
||||
@@ -322,7 +322,7 @@ The Mutelist Table must have the following columns:
|
||||
|
||||
- Checks (String): This field can contain either a Prowler Check Name or an `*` (which applies to all the scanned checks).
|
||||
|
||||
- Regions (List): This field contains a list of regions where this mutelist rule is applied (it can also contains an `*` to apply all scanned regions).
|
||||
- Regions (List): This field contains a list of regions where this mutelist rule is applied (it can also contain an `*` to apply all scanned regions).
|
||||
|
||||
- Resources (List): This field contains a list of regular expressions (regex) that applies to the resources that are wanted to be muted.
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: 'Organizations Across Cloud Providers'
|
||||
description: 'Understand organization hierarchies and onboarding across AWS, Google Cloud, and Azure'
|
||||
---
|
||||
|
||||
Cloud providers use organization-level hierarchies to group accounts, projects, or subscriptions and apply access and governance consistently. Prowler uses these hierarchies to discover cloud targets and help configure multi-account or multi-project scanning.
|
||||
|
||||
This guide explains the shared lifecycle and the differences between AWS Organizations, Google Cloud organizations, and Azure Management Groups. Use the provider-specific guides for commands, permissions, and limitations.
|
||||
|
||||
## Organization Lifecycle
|
||||
|
||||
Organization-level onboarding generally follows these steps:
|
||||
|
||||
1. **Identify the hierarchy:** Locate the organization, management account, management group, folder, organizational unit, or equivalent parent node in the cloud provider.
|
||||
2. **Grant access:** Assign the provider permissions required to enumerate the hierarchy and read the resources that Prowler scans.
|
||||
3. **Discover members:** Use Prowler to retrieve accounts, projects, or subscriptions under the selected hierarchy.
|
||||
4. **Select scan targets:** Choose the cloud targets to connect or scan. Discovery does not necessarily make every discovered target a Prowler provider.
|
||||
5. **Test access:** Confirm that Prowler can authenticate to each selected target and read its resources.
|
||||
6. **Scan and maintain:** Run scans, review findings, and repeat discovery when the provider hierarchy changes.
|
||||
|
||||
<Note>
|
||||
Organization membership changes are not automatically synchronized in every Prowler workflow. Follow the provider-specific guide to learn when manual rediscovery is required.
|
||||
</Note>
|
||||
|
||||
## Capability Matrix
|
||||
|
||||
| Capability | AWS Organizations | Google Cloud organization | Azure Management Groups |
|
||||
| --- | --- | --- | --- |
|
||||
| Hierarchy members | AWS accounts grouped in organizational units (OUs) | Projects grouped in folders and nested folders | Subscriptions grouped in management groups |
|
||||
| Organization-level discovery | Supported through AWS Organizations APIs | Supported through the Cloud Asset API | Supported through Azure management-group and subscription APIs |
|
||||
| Primary scan target | AWS account | Google Cloud project | Azure subscription |
|
||||
| Common organization-level permission | IAM role in the management or delegated administrator account | Cloud Asset Viewer or Cloud Asset Owner at the organization node | Appropriate Azure role assignment at the management-group or subscription scope |
|
||||
| Provider-specific onboarding | AWS account discovery and optional StackSet role deployment | Project discovery under an organization ID | Subscription discovery under a management group; role assignments inherit to subscriptions |
|
||||
| Membership maintenance | Repeat the discovery flow when accounts are added or removed | Re-run organization discovery when projects or folders change | Refresh discovery when subscriptions move between management groups |
|
||||
|
||||
## Provider Guides
|
||||
|
||||
### AWS Organizations
|
||||
|
||||
The [AWS Organizations guide](/user-guide/providers/aws/organizations) covers account details, delegated administration, IAM roles, CloudFormation StackSets, and CLI scanning. For Prowler Cloud onboarding, see [AWS Organizations in Prowler Cloud](/user-guide/tutorials/prowler-cloud-aws-organizations).
|
||||
|
||||
### Google Cloud Organization
|
||||
|
||||
The [Google Cloud organization guide](/user-guide/providers/gcp/organization) covers scanning projects under an organization ID, organization-level permissions, and Cloud Asset API requirements. For Prowler Cloud onboarding, see [Google Cloud organizations in Prowler Cloud](/user-guide/tutorials/prowler-cloud-gcp-organizations).
|
||||
|
||||
### Azure Management Groups
|
||||
|
||||
The [Azure Management Groups guide](/user-guide/providers/azure/management-groups) covers hierarchy setup, role assignment, subscription scope, and Azure-specific limitations. For Prowler Cloud onboarding, see [Azure Management Groups in Prowler Cloud](/user-guide/tutorials/prowler-cloud-azure-management-groups).
|
||||
|
||||
## Scope Boundaries
|
||||
|
||||
The organization concepts in this guide refer only to cloud-provider resource hierarchies:
|
||||
|
||||
- **GitHub organizations** group repositories and GitHub resources. They are a separate provider concept and are not part of AWS, Google Cloud, or Azure organization discovery.
|
||||
- **MongoDB Atlas organizations** group Atlas projects and teams. They use a separate provider API and authentication model.
|
||||
- **Prowler Cloud organizations** are internal tenants that isolate providers, scans, findings, users, and permissions. They are not the same as a cloud-provider organization and do not replace one.
|
||||
|
||||
Choose the guide that matches the hierarchy being configured, then use the relevant Prowler Cloud or CLI workflow for the scan targets.
|
||||
@@ -64,7 +64,7 @@ This method grants permanent access and is the recommended setup for production
|
||||
|
||||
7. Click "Submit" to deploy the stack
|
||||
|
||||

|
||||

|
||||
</Tab>
|
||||
<Tab title="Terraform">
|
||||
To provision the scan role using Terraform:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: 'Scanning Multiple AWS Accounts with Prowler'
|
||||
---
|
||||
|
||||
Prowler enables security scanning across multiple AWS accounts by utilizing the [Assume Role feature](/user-guide/providers/aws/role-assumption) and [integration with AWS Organizations feature](/user-guide/providers/aws/organizations).
|
||||
Prowler enables security scanning across multiple AWS accounts by using the [Assume Role feature](/user-guide/providers/aws/role-assumption) and [integration with AWS Organizations feature](/user-guide/providers/aws/organizations).
|
||||
|
||||
This approach allows execution from a single account with permissions to assume roles in the target accounts.
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ See [AWS Organizations](/user-guide/tutorials/prowler-cloud-aws-organizations) i
|
||||
|
||||
Prowler can integrate with AWS Organizations to manage the visibility and onboarding of accounts centrally.
|
||||
|
||||
For the cross-provider organization lifecycle and capability comparison, see [Organizations Across Cloud Providers](/user-guide/organizations).
|
||||
|
||||
When trusted access is enabled with the Organization, Prowler can discover accounts as they are created and even automate deployment of the Prowler Scan IAM Role.
|
||||
|
||||
> ℹ️ Trusted access can be enabled in the Management Account from the AWS Console under **AWS Organizations → Settings → Trusted access for AWS CloudFormation StackSets**.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: 'AWS Security Hub Integration with Prowler'
|
||||
---
|
||||
|
||||
Prowler natively supports **official integration** with [AWS Security Hub](https://aws.amazon.com/security-hub), allowing security findings to be sent directly. This integration enables **Prowler** to import its findings into AWS Security Hub.
|
||||
Prowler natively supports **official integration** with [AWS Security Hub](https://aws.amazon.com/security-hub), allowing security findings to be sent directly. This integration enables **Prowler** to import its findings into AWS Security Hub.
|
||||
|
||||
To activate the integration, follow these steps in at least one AWS region within your AWS account:
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: 'Azure Management Groups in Prowler'
|
||||
---
|
||||
|
||||
Azure Management Groups provide a hierarchy above subscriptions. They allow Azure role assignments and governance policies to apply to multiple subscriptions through a shared scope.
|
||||
|
||||
For the cross-provider concepts and lifecycle, see [Organizations Across Cloud Providers](/user-guide/organizations).
|
||||
|
||||
## Azure Hierarchy
|
||||
|
||||
Azure resources are organized in the following order:
|
||||
|
||||
1. Tenant
|
||||
2. Management groups
|
||||
3. Subscriptions
|
||||
4. Resource groups
|
||||
5. Resources
|
||||
|
||||
Prowler scans Azure subscriptions. Management groups help organize those subscriptions and provide a scope where permissions can be assigned, but a management group is not itself a scan target.
|
||||
|
||||
## Create a Management Group
|
||||
|
||||
To create a management group, follow the [official Azure guide](https://learn.microsoft.com/en-us/azure/governance/management-groups/create-management-group-portal).
|
||||
|
||||

|
||||
|
||||
After creating the management group, add the subscriptions that Prowler should access and scan.
|
||||
|
||||

|
||||
|
||||
## Assign Roles
|
||||
|
||||
Assign the roles required by Prowler at the management-group scope instead of assigning them separately to every subscription. Role assignments at a management group can inherit to its child subscriptions, subject to Azure role-assignment and inheritance rules.
|
||||
|
||||
Use the [subscription scope permissions](/user-guide/providers/azure/authentication#subscription-scope-permissions) guide to identify the permissions required for scans. The identity used by Prowler must be able to read the management-group hierarchy and access each subscription selected for scanning.
|
||||
|
||||
## Subscription Scope
|
||||
|
||||
Management groups organize subscriptions, but Azure scan results remain scoped to individual subscriptions:
|
||||
|
||||
- Prowler Cloud scans one subscription per scan.
|
||||
- Prowler CLI can scan multiple subscriptions by using the `--subscription-ids` option.
|
||||
- A subscription must be accessible to the configured identity before Prowler can scan it.
|
||||
- Moving a subscription between management groups can change the permissions it inherits and may require a connection test or rediscovery.
|
||||
|
||||
See [Azure Subscription Scope](/user-guide/providers/azure/subscriptions) for subscription selection and CLI options.
|
||||
|
||||
## Limitations
|
||||
|
||||
- Management groups do not replace subscription providers in Prowler.
|
||||
- Azure role inheritance depends on the management-group hierarchy and the scope of each assignment; verify access on every subscription selected for scanning.
|
||||
- The Prowler Cloud workflow is designed around Azure management-group discovery and subscription onboarding. The Prowler CLI workflow still requires explicit subscription selection when restricting scans.
|
||||
- Changes to management-group membership or role assignments may not be reflected until the hierarchy is refreshed and access is tested again.
|
||||
@@ -25,14 +25,4 @@ Check the [Authentication > Subscription Scope Permissions](/user-guide/provider
|
||||
|
||||
## Recommendation for Managing Multiple Subscriptions
|
||||
|
||||
Scanning multiple subscriptions requires creating and assigning roles for each, which can be a time-consuming process. To streamline subscription management and auditing, use management groups in Azure. This approach allows Prowler to efficiently organize and audit multiple subscriptions collectively.
|
||||
|
||||
1. **Create a Management Group**: Follow the [official guide](https://learn.microsoft.com/en-us/azure/governance/management-groups/create-management-group-portal) to create a new management group.
|
||||
|
||||

|
||||
|
||||
2. **Assign Roles**: Assign necessary roles to the management group, similar to the [role assignment process](#assigning-permissions-for-subscription-scans).
|
||||
|
||||
Role assignment should be done at the management group level instead of per subscription.
|
||||
|
||||
3. **Add Subscriptions**: Add all subscriptions you want to audit to the newly created management group. 
|
||||
Scanning multiple subscriptions requires creating and assigning roles for each, which can be a time-consuming process. To streamline subscription management and auditing, use [Azure Management Groups](/user-guide/providers/azure/management-groups) to organize subscriptions and assign permissions collectively.
|
||||
|
||||
@@ -4,6 +4,8 @@ title: 'Scanning a Specific GCP Organization'
|
||||
|
||||
By default, Prowler scans all Google Cloud projects accessible to the authenticated user.
|
||||
|
||||
For the cross-provider organization lifecycle and capability comparison, see [Organizations Across Cloud Providers](/user-guide/organizations).
|
||||
|
||||
To limit the scan to projects within a specific Google Cloud organization, use the `--organization-id` option with the GCP organization’s ID:
|
||||
|
||||
```console
|
||||
|
||||
@@ -42,7 +42,7 @@ Required for scanning repository security settings:
|
||||
|
||||
| Permission | Access Level | Purpose | Checks Enabled |
|
||||
|------------|-------------|---------|----------------|
|
||||
| **Administration** | Read | Branch protection, security settings | All branch protection checks, secret scanning status |
|
||||
| **Administration** | Read | Branch protection, security and Actions settings | All branch protection checks, secret scanning status, `repository_default_workflow_permissions_read_only` |
|
||||
| **Contents** | Read | File existence checks | `repository_public_has_securitymd_file`, `repository_has_codeowners_file` |
|
||||
| **Metadata** | Read | Basic repository information | All checks (automatically granted) |
|
||||
| **Dependabot alerts** | Read | Dependency vulnerability scanning | `repository_dependency_scanning_enabled` |
|
||||
@@ -63,7 +63,7 @@ Required for scanning organization-level security settings:
|
||||
|
||||
| Permission | Access Level | Purpose | Checks Enabled |
|
||||
|------------|-------------|---------|----------------|
|
||||
| **Administration** | Read | Organization security policies | `organization_members_mfa_required`, `organization_repository_creation_limited`, `organization_default_repository_permission_strict` |
|
||||
| **Administration** | Read | Organization security policies and Actions settings | `organization_members_mfa_required`, `organization_repository_creation_limited`, `organization_default_repository_permission_strict`, `organization_default_workflow_permissions_read_only`, `organization_actions_pull_request_approval_disabled` |
|
||||
| **Members** | Read | Member access reviews | Organization membership auditing |
|
||||
|
||||
#### Account Permissions (Fine-Grained PAT only)
|
||||
@@ -83,8 +83,8 @@ With the **Read-only permissions** listed above, Prowler can run:
|
||||
| Check Category | Coverage | Notes |
|
||||
|----------------|----------|-------|
|
||||
| Branch protection checks (12 checks) | ✅ Full | Signed commits, status checks, PR reviews, etc. |
|
||||
| Repository security checks | ✅ Full | Secret scanning, Dependabot, SECURITY.md, CODEOWNERS |
|
||||
| Organization checks (3 checks) | ✅ Full | MFA, repo creation policies, default permissions |
|
||||
| Repository security checks | ✅ Full | Secret scanning, Dependabot, SECURITY.md, CODEOWNERS, default workflow permissions |
|
||||
| Organization checks (5 checks) | ✅ Full | MFA, repo creation policies, default permissions, Actions workflow permissions |
|
||||
| Compliance frameworks | ✅ Full | CIS GitHub Benchmark and others |
|
||||
| Merge settings (`delete_branch_on_merge`) | ⚠️ MANUAL | Requires write permission (see below) |
|
||||
|
||||
@@ -171,6 +171,7 @@ Use OAuth App Tokens when building applications that need delegated user permiss
|
||||
|
||||
- `repo`: Full control of repositories
|
||||
- `read:org`: Read organization and team membership
|
||||
- `admin:org`: Required by `organization_default_workflow_permissions_read_only` and `organization_actions_pull_request_approval_disabled` to read the organization Actions workflow permissions
|
||||
- `read:user`: Read user profile data
|
||||
|
||||
**Create an OAuth App:**
|
||||
@@ -214,7 +215,7 @@ If a GitHub App is required:
|
||||
|
||||
| Permission | Access Level | Purpose | Checks Enabled |
|
||||
|------------|-------------|---------|----------------|
|
||||
| **Administration** | Read | Branch protection, security settings | All branch protection checks, `repository_secret_scanning_enabled` |
|
||||
| **Administration** | Read | Branch protection, security and Actions settings | All branch protection checks, `repository_secret_scanning_enabled`, `repository_default_workflow_permissions_read_only` |
|
||||
| **Contents** | Read | File existence checks | `repository_public_has_securitymd_file`, `repository_has_codeowners_file` |
|
||||
| **Metadata** | Read | Basic repository information | All checks (automatically granted) |
|
||||
| **Dependabot alerts** | Read | Dependency vulnerability scanning | `repository_dependency_scanning_enabled` |
|
||||
@@ -223,7 +224,7 @@ If a GitHub App is required:
|
||||
|
||||
| Permission | Access Level | Purpose | Checks Enabled |
|
||||
|------------|-------------|---------|----------------|
|
||||
| **Administration** | Read | Organization security policies | `organization_members_mfa_required`, `organization_repository_creation_limited`, `organization_default_repository_permission_strict` |
|
||||
| **Administration** | Read | Organization security policies and Actions settings | `organization_members_mfa_required`, `organization_repository_creation_limited`, `organization_default_repository_permission_strict`, `organization_default_workflow_permissions_read_only`, `organization_actions_pull_request_approval_disabled` |
|
||||
| **Members** | Read | Member access reviews | Organization membership auditing |
|
||||
|
||||
**Create a GitHub App:**
|
||||
|
||||
@@ -39,10 +39,12 @@ When using service principal authentication, add these **Application Permissions
|
||||
|
||||
**Microsoft Graph API Permissions:**
|
||||
|
||||
- `AccessReview.Read.All`: Required for `entra_access_review_guest_users_configured` and `entra_access_review_privileged_roles_configured` checks. A Microsoft Entra ID P2 or Microsoft Entra ID Governance license is also required.
|
||||
- `AuditLog.Read.All`: Required for Entra service.
|
||||
- `Directory.Read.All`: Required for all services.
|
||||
- `OnPremDirectorySynchronization.Read.All`: Required for `entra_seamless_sso_disabled` check (hybrid deployments).
|
||||
- `Policy.Read.All`: Required for all services.
|
||||
- `RoleManagementPolicy.Read.Directory`: Required for `entra_pim_global_administrator_approval_required` and `entra_pim_privileged_role_administrator_approval_required` checks. A Microsoft Entra ID P2 or Microsoft Entra ID Governance license is also required.
|
||||
- `SecurityIdentitiesHealth.Read.All`: Required for `defenderidentity_health_issues_no_open` check.
|
||||
- `SecurityIdentitiesSensors.Read.All`: Required for `defenderidentity_health_issues_no_open` check.
|
||||
- `SharePointTenantSettings.Read.All`: Required for SharePoint service.
|
||||
@@ -110,10 +112,12 @@ Browser and Azure CLI authentication methods limit scanning capabilities to chec
|
||||
|
||||
3. Search and select the required permissions:
|
||||
|
||||
- `AccessReview.Read.All`: Required for `entra_access_review_guest_users_configured` and `entra_access_review_privileged_roles_configured` checks. A Microsoft Entra ID P2 or Microsoft Entra ID Governance license is also required
|
||||
- `AuditLog.Read.All`: Required for Entra service
|
||||
- `Directory.Read.All`: Required for all services
|
||||
- `OnPremDirectorySynchronization.Read.All`: Required for `entra_seamless_sso_disabled` check (hybrid deployments)
|
||||
- `Policy.Read.All`: Required for all services
|
||||
- `RoleManagementPolicy.Read.Directory`: Required for `entra_pim_global_administrator_approval_required` and `entra_pim_privileged_role_administrator_approval_required` checks. A Microsoft Entra ID P2 or Microsoft Entra ID Governance license is also required
|
||||
- `SecurityIdentitiesHealth.Read.All`: Required for `defenderidentity_health_issues_no_open` check
|
||||
- `SecurityIdentitiesSensors.Read.All`: Required for `defenderidentity_health_issues_no_open` check
|
||||
- `SharePointTenantSettings.Read.All`: Required for SharePoint service
|
||||
|
||||
@@ -34,4 +34,4 @@ prowler scaleway
|
||||
|
||||
## Required Scaleway Permissions
|
||||
|
||||
The API key bearer needs read access to the IAM API in order to list users and API keys. The `IAMReadOnly` policy is sufficient. Refer to the [Scaleway IAM policy reference](https://www.scaleway.com/en/docs/identity-and-access-management/iam/reference-content/permission-sets/) for the full list of permissions.
|
||||
The API key bearer needs read access to the IAM API to list users and API keys. The `IAMReadOnly` policy is sufficient. Refer to the [Scaleway IAM policy reference](https://www.scaleway.com/en/docs/identity-and-access-management/iam/reference-content/permission-sets/) for the full list of permissions.
|
||||
|
||||
@@ -13,7 +13,7 @@ StackIT support in Prowler is community-maintained. For commercial support or to
|
||||
Before running Prowler with the StackIT provider, ensure you have:
|
||||
|
||||
1. A StackIT account with at least one project
|
||||
2. A StackIT service account key file with permissions on the project (`iaas.viewer` is enough for the currently shipped IaaS checks; `project.owner` works for any future service). See the [Authentication guide](/user-guide/providers/stackit/authentication) for the full setup.
|
||||
2. A StackIT service account key file with project permissions for the services to scan. `iaas.viewer` is sufficient for the currently shipped IaaS checks. For SKE, assign the project-scoped `Reader` role as a broad read-only convenience, or create a custom role with `resource-manager.project.get`, `ske.cluster.list`, `ske.cluster.get`, and `ske.version.list`. The `resource-manager.project.get` permission is optional for scanning; it only lets Prowler verify and display the project name. See the [Authentication guide](/user-guide/providers/stackit/authentication) for the full setup.
|
||||
3. Access to Prowler CLI (see [Installation](/getting-started/installation/prowler-cli))
|
||||
|
||||
## Prowler CLI
|
||||
@@ -124,7 +124,9 @@ Prowler outputs findings to the console and writes reports to the `output/` dire
|
||||
|
||||
| Service | StackIT API | Description | Example Checks |
|
||||
|---------|-------------|-------------|----------------|
|
||||
| **IaaS** | `iaas` | Virtual machines, network interfaces, security groups | `iaas_security_group_ssh_unrestricted`, `iaas_security_group_rdp_unrestricted`, `iaas_security_group_database_unrestricted`, `iaas_security_group_all_traffic_unrestricted` |
|
||||
| **IaaS** | `iaas` | Virtual machines, network interfaces, security groups | `iaas_security_group_ssh_unrestricted`, `iaas_security_group_rdp_unrestricted`, `iaas_security_group_database_unrestricted`, `iaas_security_group_all_traffic_unrestricted`, `iaas_server_public_ip_attached` |
|
||||
| **Object Storage** | `objectstorage` | Buckets, object lock, retention policies, access keys | `objectstorage_bucket_object_lock_enabled`, `objectstorage_bucket_retention_policy`, `objectstorage_access_key_expiration` |
|
||||
| **Kubernetes Engine** | `ske` | Managed Kubernetes clusters | `ske_cluster_no_public_endpoint` |
|
||||
|
||||
Additional services will be added in future releases. Track progress in the [Prowler release notes](https://github.com/prowler-cloud/prowler/releases).
|
||||
|
||||
@@ -136,6 +138,8 @@ If the scan fails with a 401 error, the service account key is no longer valid (
|
||||
|
||||
### Permission Errors
|
||||
|
||||
If checks fail with a 403 error, the service account is missing the required role on the project. Re-check the role assignment in the StackIT portal (`iaas.viewer` is the minimum for the shipped IaaS checks).
|
||||
If the connection probe warns that Resource Manager access could not be verified with a 403 response, Prowler continues to service discovery. Grant the optional `resource-manager.project.get` permission to verify and display the project name.
|
||||
|
||||
If a service call fails with a 403 response, the service account is missing a permission required by that service. `iaas.viewer` is sufficient for the shipped IaaS checks. The `ske_cluster_no_public_endpoint` check lists clusters and therefore requires `ske.cluster.list`; STACKIT least-privilege custom-role examples also include `ske.cluster.get` and `ske.version.list`. Service API calls remain authoritative and stop the scan when access is denied.
|
||||
|
||||
For detailed setup steps, see the [Authentication guide](/user-guide/providers/stackit/authentication).
|
||||
|
||||
@@ -4,6 +4,8 @@ title: 'AWS Organizations Bulk Provisioning in Prowler'
|
||||
|
||||
Prowler offers an automated tool to discover and provision all AWS accounts within an AWS Organization. This streamlines onboarding for organizations managing multiple AWS accounts by automatically generating the configuration needed for bulk provisioning.
|
||||
|
||||
For the cross-provider organization lifecycle and terminology, see [Organizations Across Cloud Providers](/user-guide/organizations).
|
||||
|
||||
The tool, `aws_org_generator.py`, complements the [Bulk Provider Provisioning](./bulk-provider-provisioning) tool and is available in the Prowler repository at: [util/prowler-bulk-provisioning](https://github.com/prowler-cloud/prowler/tree/master/util/prowler-bulk-provisioning)
|
||||
|
||||
<Note>
|
||||
|
||||