fix: push
No files matched your search
@@ -0,0 +1,89 @@
|
||||
#### Important Note ####
|
||||
# This file is used to store environment variables for the Prowler App.
|
||||
# For production, it is recommended to use a secure method to store these variables and change the default secret keys.
|
||||
|
||||
#### Prowler UI Configuration ####
|
||||
PROWLER_UI_VERSION="latest"
|
||||
SITE_URL=http://localhost:3000
|
||||
API_BASE_URL=http://prowler-api:8080/api/v1
|
||||
AUTH_TRUST_HOST=true
|
||||
UI_PORT=3000
|
||||
# openssl rand -base64 32
|
||||
AUTH_SECRET="N/c6mnaS5+SWq81+819OrzQZlmx1Vxtp/orjttJSmw8="
|
||||
|
||||
#### Prowler API Configuration ####
|
||||
PROWLER_API_VERSION="latest"
|
||||
# PostgreSQL settings
|
||||
# If running Django and celery on host, use 'localhost', else use 'postgres-db'
|
||||
POSTGRES_HOST=postgres-db
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_ADMIN_USER=prowler_admin
|
||||
POSTGRES_ADMIN_PASSWORD=postgres
|
||||
POSTGRES_USER=prowler
|
||||
POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_DB=prowler_db
|
||||
|
||||
# Valkey settings
|
||||
# If running Valkey and celery on host, use localhost, else use 'valkey'
|
||||
VALKEY_HOST=valkey
|
||||
VALKEY_PORT=6379
|
||||
VALKEY_DB=0
|
||||
|
||||
# Django settings
|
||||
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,prowler-api
|
||||
DJANGO_BIND_ADDRESS=0.0.0.0
|
||||
DJANGO_PORT=8080
|
||||
DJANGO_DEBUG=False
|
||||
DJANGO_SETTINGS_MODULE=config.django.production
|
||||
# Select one of [ndjson|human_readable]
|
||||
DJANGO_LOGGING_FORMATTER=human_readable
|
||||
# Select one of [DEBUG|INFO|WARNING|ERROR|CRITICAL]
|
||||
# Applies to both Django and Celery Workers
|
||||
DJANGO_LOGGING_LEVEL=INFO
|
||||
DJANGO_WORKERS=4 # Defaults to the maximum available based on CPU cores if not set.
|
||||
DJANGO_ACCESS_TOKEN_LIFETIME=30 # Token lifetime is in minutes
|
||||
DJANGO_REFRESH_TOKEN_LIFETIME=1440 # Token lifetime is in minutes
|
||||
DJANGO_CACHE_MAX_AGE=3600
|
||||
DJANGO_STALE_WHILE_REVALIDATE=60
|
||||
DJANGO_MANAGE_DB_PARTITIONS=True
|
||||
# openssl genrsa -out private.pem 2048
|
||||
DJANGO_TOKEN_SIGNING_KEY="-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDs4e+kt7SnUJek
|
||||
6V5r9zMGzXCoU5qnChfPiqu+BgANyawz+MyVZPs6RCRfeo6tlCknPQtOziyXYM2I
|
||||
7X+qckmuzsjqp8+u+o1mw3VvUuJew5k2SQLPYwsiTzuFNVJEOgRo3hywGiGwS2iv
|
||||
/5nh2QAl7fq2qLqZEXQa5+/xJlQggS1CYxOJgggvLyra50QZlBvPve/AxKJ/EV/Q
|
||||
irWTZU5lLNI8sH2iZR05vQeBsxZ0dCnGMT+vGl+cGkqrvzQzKsYbDmabMcfTYhYi
|
||||
78fpv6A4uharJFHayypYBjE39PwhMyyeycrNXlpm1jpq+03HgmDuDMHydk1tNwuT
|
||||
nEC7m7iNAgMBAAECggEAA2m48nJcJbn9SVi8bclMwKkWmbJErOnyEGEy2sTK3Of+
|
||||
NWx9BB0FmqAPNxn0ss8K7cANKOhDD7ZLF9E2MO4/HgfoMKtUzHRbM7MWvtEepldi
|
||||
nnvcUMEgULD8Dk4HnqiIVjt3BdmGiTv46OpBnRWrkSBV56pUL+7msZmMZTjUZvh2
|
||||
ZWv0+I3gtDIjo2Zo/FiwDV7CfwRjJarRpYUj/0YyuSA4FuOUYl41WAX1I301FKMH
|
||||
xo3jiAYi1s7IneJ16OtPpOA34Wg5F6ebm/UO0uNe+iD4kCXKaZmxYQPh5tfB0Qa3
|
||||
qj1T7GNpFNyvtG7VVdauhkb8iu8X/wl6PCwbg0RCKQKBgQD9HfpnpH0lDlHMRw9K
|
||||
X7Vby/1fSYy1BQtlXFEIPTN/btJ/asGxLmAVwJ2HAPXWlrfSjVAH7CtVmzN7v8oj
|
||||
HeIHfeSgoWEu1syvnv2AMaYSo03UjFFlfc/GUxF7DUScRIhcJUPCP8jkAROz9nFv
|
||||
DByNjUL17Q9r43DmDiRsy0IFqQKBgQDvlJ9Uhl+Sp7gRgKYwa/IG0+I4AduAM+Gz
|
||||
Dxbm52QrMGMTjaJFLmLHBUZ/ot+pge7tZZGws8YR8ufpyMJbMqPjxhIvRRa/p1Tf
|
||||
E3TQPW93FMsHUvxAgY3MV5MzXFPhlNAKb+akP/RcXUhetGAuZKLubtDCWa55ZQuL
|
||||
wj2OS+niRQKBgE7K8zUqNi6/22S8xhy/2GPgB1qPObbsABUofK0U6CAGLo6te+gc
|
||||
6Jo84IyzFtQbDNQFW2Fr+j1m18rw9AqkdcUhQndiZS9AfG07D+zFB86LeWHt4DS4
|
||||
ymIRX8Kvaak/iDcu/n3Mf0vCrhB6aetImObTj4GgrwlFvtJOmrYnO8EpAoGAIXXP
|
||||
Xt25gWD9OyyNiVu6HKwA/zN7NYeJcRmdaDhO7B1A6R0x2Zml4AfjlbXoqOLlvLAf
|
||||
zd79vcoAC82nH1eOPiSOq51plPDI0LMF8IN0CtyTkn1Lj7LIXA6rF1RAvtOqzppc
|
||||
SvpHpZK9pcRpXnFdtBE0BMDDtl6fYzCIqlP94UUCgYEAnhXbAQMF7LQifEm34Dx8
|
||||
BizRMOKcqJGPvbO2+Iyt50O5X6onU2ITzSV1QHtOvAazu+B1aG9pEuBFDQ+ASxEu
|
||||
L9ruJElkOkb/o45TSF6KCsHd55ReTZ8AqnRjf5R+lyzPqTZCXXb8KTcRvWT4zQa3
|
||||
VxyT2PnaSqEcexWUy4+UXoQ=
|
||||
-----END PRIVATE KEY-----"
|
||||
# openssl rsa -in private.pem -pubout -out public.pem
|
||||
DJANGO_TOKEN_VERIFYING_KEY="-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7OHvpLe0p1CXpOlea/cz
|
||||
Bs1wqFOapwoXz4qrvgYADcmsM/jMlWT7OkQkX3qOrZQpJz0LTs4sl2DNiO1/qnJJ
|
||||
rs7I6qfPrvqNZsN1b1LiXsOZNkkCz2MLIk87hTVSRDoEaN4csBohsEtor/+Z4dkA
|
||||
Je36tqi6mRF0Gufv8SZUIIEtQmMTiYIILy8q2udEGZQbz73vwMSifxFf0Iq1k2VO
|
||||
ZSzSPLB9omUdOb0HgbMWdHQpxjE/rxpfnBpKq780MyrGGw5mmzHH02IWIu/H6b+g
|
||||
OLoWqyRR2ssqWAYxN/T8ITMsnsnKzV5aZtY6avtNx4Jg7gzB8nZNbTcLk5xAu5u4
|
||||
jQIDAQAB
|
||||
-----END PUBLIC KEY-----"
|
||||
# openssl rand -base64 32
|
||||
DJANGO_SECRETS_ENCRYPTION_KEY="oE/ltOhp/n1TdbHjVmzcjDPLcLA41CVI/4Rk+UB5ESc="
|
||||
@@ -1,4 +1,4 @@
|
||||
name: "API - Pull Request"
|
||||
name: "API - Pull Request"
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -148,7 +148,7 @@ jobs:
|
||||
working-directory: ./api
|
||||
if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true'
|
||||
run: |
|
||||
poetry run pytest -n auto --cov=./src/backend --cov-report=xml src/backend
|
||||
poetry run pytest --cov=./src/backend --cov-report=xml src/backend
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true'
|
||||
uses: codecov/codecov-action@v5
|
||||
|
||||
@@ -12,6 +12,7 @@ build/
|
||||
/dist/
|
||||
*.egg-info/
|
||||
*/__pycache__/*.pyc
|
||||
.idea/
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
@@ -46,7 +47,8 @@ junit-reports/
|
||||
*.tfstate
|
||||
|
||||
# .env
|
||||
.env*
|
||||
ui/.env*
|
||||
api/.env*
|
||||
|
||||
# Coverage
|
||||
.coverage*
|
||||
@@ -55,3 +57,6 @@ coverage*
|
||||
|
||||
# Node
|
||||
node_modules
|
||||
|
||||
# Persistent data
|
||||
_data/
|
||||
@@ -97,12 +97,13 @@ repos:
|
||||
- id: safety
|
||||
name: safety
|
||||
description: "Safety is a tool that checks your installed dependencies for known security vulnerabilities"
|
||||
entry: bash -c 'safety check --ignore 70612'
|
||||
entry: bash -c 'safety check --ignore 70612,66963'
|
||||
language: system
|
||||
|
||||
- id: vulture
|
||||
name: vulture
|
||||
description: "Vulture finds unused code in Python programs."
|
||||
entry: bash -c 'vulture --exclude "contrib" --min-confidence 100 .'
|
||||
exclude: 'api/src/backend/'
|
||||
language: system
|
||||
files: '.*\.py'
|
||||
@@ -45,6 +45,14 @@
|
||||
|
||||
**Prowler** is an Open Source security tool to perform AWS, Azure, Google Cloud and Kubernetes security best practices assessments, audits, incident response, continuous monitoring, hardening and forensics readiness, and also remediations! We have Prowler CLI (Command Line Interface) that we call Prowler Open Source and a service on top of it that we call <a href="https://prowler.com">Prowler SaaS</a>.
|
||||
|
||||
## Prowler App
|
||||
|
||||
Prowler App is a web application that allows you to run Prowler in your cloud provider accounts and visualize the results in a user-friendly interface.
|
||||
|
||||

|
||||
|
||||
>More details at [Prowler App Documentation](https://docs.prowler.com/projects/prowler-open-source/en/latest/#prowler-app-installation)
|
||||
|
||||
## Prowler CLI
|
||||
|
||||
```console
|
||||
@@ -63,42 +71,121 @@ It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, Fe
|
||||
|
||||
| Provider | Checks | Services | [Compliance Frameworks](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/compliance/) | [Categories](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/misc/#categories) |
|
||||
|---|---|---|---|---|
|
||||
| AWS | 553 | 77 -> `prowler aws --list-services` | 30 -> `prowler aws --list-compliance` | 9 -> `prowler aws --list-categories` |
|
||||
| AWS | 561 | 81 -> `prowler aws --list-services` | 30 -> `prowler aws --list-compliance` | 9 -> `prowler aws --list-categories` |
|
||||
| GCP | 77 | 13 -> `prowler gcp --list-services` | 3 -> `prowler gcp --list-compliance` | 2 -> `prowler gcp --list-categories`|
|
||||
| Azure | 138 | 17 -> `prowler azure --list-services` | 4 -> `prowler azure --list-compliance` | 2 -> `prowler azure --list-categories` |
|
||||
| Azure | 139 | 18 -> `prowler azure --list-services` | 4 -> `prowler azure --list-compliance` | 2 -> `prowler azure --list-categories` |
|
||||
| Kubernetes | 83 | 7 -> `prowler kubernetes --list-services` | 1 -> `prowler kubernetes --list-compliance` | 7 -> `prowler kubernetes --list-categories` |
|
||||
|
||||
# 💻 Installation
|
||||
|
||||
## Pip package
|
||||
Prowler is available as a project in [PyPI](https://pypi.org/project/prowler-cloud/), thus can be installed using pip with Python >= 3.9, < 3.13:
|
||||
## Prowler App
|
||||
|
||||
Prowler App can be installed in different ways, depending on your environment:
|
||||
|
||||
> See how to use Prowler App in the [Prowler App Usage Guide](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/prowler-app/).
|
||||
|
||||
### Docker Compose
|
||||
|
||||
**Requirements**
|
||||
|
||||
* `Docker Compose` installed: https://docs.docker.com/compose/install/.
|
||||
|
||||
**Commands**
|
||||
|
||||
``` console
|
||||
curl -LO https://raw.githubusercontent.com/prowler-cloud/prowler/refs/heads/master/docker-compose.yml
|
||||
curl -LO https://raw.githubusercontent.com/prowler-cloud/prowler/refs/heads/master/.env
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
> Enjoy Prowler App at http://localhost:3000 by signing up with your email and password.
|
||||
|
||||
### From GitHub
|
||||
|
||||
**Requirements**
|
||||
|
||||
* `git` installed.
|
||||
* `poetry` installed: [poetry installation](https://python-poetry.org/docs/#installation).
|
||||
* `npm` installed: [npm installation](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).
|
||||
* `Docker Compose` installed: https://docs.docker.com/compose/install/.
|
||||
|
||||
**Commands to run the API**
|
||||
|
||||
``` console
|
||||
git clone https://github.com/prowler-cloud/prowler
|
||||
cd prowler/api
|
||||
poetry install
|
||||
poetry shell
|
||||
set -a
|
||||
source .env
|
||||
docker compose up postgres valkey -d
|
||||
cd src/backend
|
||||
python manage.py migrate --database admin
|
||||
gunicorn -c config/guniconf.py config.wsgi:application
|
||||
```
|
||||
|
||||
> Now, you can access the API documentation at http://localhost:8080/api/v1/docs.
|
||||
|
||||
**Commands to run the API Worker**
|
||||
|
||||
``` console
|
||||
git clone https://github.com/prowler-cloud/prowler
|
||||
cd prowler/api
|
||||
poetry install
|
||||
poetry shell
|
||||
set -a
|
||||
source .env
|
||||
cd src/backend
|
||||
python -m celery -A config.celery worker -l info -E
|
||||
```
|
||||
|
||||
**Commands to run the UI**
|
||||
|
||||
``` console
|
||||
git clone https://github.com/prowler-cloud/prowler
|
||||
cd prowler/ui
|
||||
npm install
|
||||
npm run build
|
||||
npm start
|
||||
```
|
||||
|
||||
> Enjoy Prowler App at http://localhost:3000 by signing up with your email and password.
|
||||
|
||||
## Prowler CLI
|
||||
### Pip package
|
||||
Prowler CLI is available as a project in [PyPI](https://pypi.org/project/prowler-cloud/), thus can be installed using pip with Python >= 3.9, < 3.13:
|
||||
|
||||
```console
|
||||
pip install prowler
|
||||
prowler -v
|
||||
```
|
||||
>More details at [https://docs.prowler.com](https://docs.prowler.com/projects/prowler-open-source/en/latest/)
|
||||
>More details at [https://docs.prowler.com](https://docs.prowler.com/projects/prowler-open-source/en/latest/#prowler-cli-installation)
|
||||
|
||||
## Containers
|
||||
### Containers
|
||||
|
||||
The available versions of Prowler are the following:
|
||||
The available versions of Prowler CLI are the following:
|
||||
|
||||
- `latest`: in sync with `master` branch (bear in mind that it is not a stable version)
|
||||
- `v4-latest`: in sync with `v4` branch (bear in mind that it is not a stable version)
|
||||
- `v3-latest`: in sync with `v3` branch (bear in mind that it is not a stable version)
|
||||
- `<x.y.z>` (release): you can find the releases [here](https://github.com/prowler-cloud/prowler/releases), those are stable releases.
|
||||
- `stable`: this tag always point to the latest release.
|
||||
- `v4-stable`: this tag always point to the latest release for v4.
|
||||
- `v3-stable`: this tag always point to the latest release for v3.
|
||||
|
||||
The container images are available here:
|
||||
- Prowler CLI:
|
||||
- [DockerHub](https://hub.docker.com/r/toniblyx/prowler/tags)
|
||||
- [AWS Public ECR](https://gallery.ecr.aws/prowler-cloud/prowler)
|
||||
- Prowler App:
|
||||
- [DockerHub - Prowler UI](https://hub.docker.com/r/prowlercloud/prowler-ui/tags)
|
||||
- [DockerHub - Prowler API](https://hub.docker.com/r/prowlercloud/prowler-api/tags)
|
||||
|
||||
- [DockerHub](https://hub.docker.com/r/toniblyx/prowler/tags)
|
||||
- [AWS Public ECR](https://gallery.ecr.aws/prowler-cloud/prowler)
|
||||
|
||||
## From GitHub
|
||||
### From GitHub
|
||||
|
||||
Python >= 3.9, < 3.13 is required with pip and poetry:
|
||||
|
||||
```
|
||||
``` console
|
||||
git clone https://github.com/prowler-cloud/prowler
|
||||
cd prowler
|
||||
poetry shell
|
||||
@@ -108,6 +195,16 @@ python prowler.py -v
|
||||
> If you want to clone Prowler from Windows, use `git config core.longpaths true` to allow long file paths.
|
||||
# 📐✏️ High level architecture
|
||||
|
||||
## Prowler App
|
||||
The **Prowler App** consists of three main components:
|
||||
|
||||
- **Prowler UI**: A user-friendly web interface for running Prowler and viewing results, powered by Next.js.
|
||||
- **Prowler API**: The backend API that executes Prowler scans and stores the results, built with Django REST Framework.
|
||||
- **Prowler SDK**: A Python SDK that integrates with the Prowler CLI for advanced functionality.
|
||||
|
||||

|
||||
|
||||
## Prowler CLI
|
||||
You can run Prowler from your workstation, a Kubernetes Job, a Google Compute Engine, an Azure VM, an EC2 instance, Fargate or any other container, CloudShell and many more.
|
||||
|
||||

|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
services:
|
||||
api-dev:
|
||||
hostname: "prowler-api"
|
||||
build:
|
||||
context: ./api
|
||||
dockerfile: Dockerfile
|
||||
target: dev
|
||||
environment:
|
||||
- DJANGO_SETTINGS_MODULE=config.django.devel
|
||||
- DJANGO_LOGGING_FORMATTER=${LOGGING_FORMATTER:-human_readable}
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
ports:
|
||||
- "${DJANGO_PORT:-8080}:${DJANGO_PORT:-8080}"
|
||||
volumes:
|
||||
- "./api/src/backend:/home/prowler/backend"
|
||||
- "./api/pyproject.toml:/home/prowler/pyproject.toml"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
valkey:
|
||||
condition: service_healthy
|
||||
entrypoint:
|
||||
- "/home/prowler/docker-entrypoint.sh"
|
||||
- "dev"
|
||||
|
||||
ui-dev:
|
||||
build:
|
||||
context: ./ui
|
||||
dockerfile: Dockerfile
|
||||
target: dev
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
ports:
|
||||
- 3000:3000
|
||||
|
||||
postgres:
|
||||
image: postgres:16.3-alpine
|
||||
hostname: "postgres-db"
|
||||
volumes:
|
||||
- ./_data/postgres:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_ADMIN_USER}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_ADMIN_PASSWORD}
|
||||
- POSTGRES_DB=${POSTGRES_DB}
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:${POSTGRES_PORT:-5432}"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "sh -c 'pg_isready -U ${POSTGRES_ADMIN_USER} -d ${POSTGRES_DB}'"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
valkey:
|
||||
image: valkey/valkey:7-alpine3.19
|
||||
hostname: "valkey"
|
||||
volumes:
|
||||
- ./api/_data/valkey:/data
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "sh -c 'valkey-cli ping'"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
worker-dev:
|
||||
build:
|
||||
context: ./api
|
||||
dockerfile: Dockerfile
|
||||
target: dev
|
||||
environment:
|
||||
- DJANGO_SETTINGS_MODULE=config.django.devel
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
depends_on:
|
||||
valkey:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
entrypoint:
|
||||
- "/home/prowler/docker-entrypoint.sh"
|
||||
- "worker"
|
||||
|
||||
worker-beat:
|
||||
build:
|
||||
context: ./api
|
||||
dockerfile: Dockerfile
|
||||
target: dev
|
||||
environment:
|
||||
- DJANGO_SETTINGS_MODULE=config.django.devel
|
||||
env_file:
|
||||
- path: ./.env
|
||||
required: false
|
||||
depends_on:
|
||||
valkey:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
entrypoint:
|
||||
- "../docker-entrypoint.sh"
|
||||
- "beat"
|
||||
@@ -0,0 +1,89 @@
|
||||
services:
|
||||
api:
|
||||
hostname: "prowler-api"
|
||||
image: prowlercloud/prowler-api:${PROWLER_API_VERSION:-latest}
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
ports:
|
||||
- "${DJANGO_PORT:-8080}:${DJANGO_PORT:-8080}"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
valkey:
|
||||
condition: service_healthy
|
||||
entrypoint:
|
||||
- "/home/prowler/docker-entrypoint.sh"
|
||||
- "prod"
|
||||
|
||||
ui:
|
||||
image: prowlercloud/prowler-ui:${PROWLER_UI_VERSION:-latest}
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
ports:
|
||||
- ${UI_PORT:-3000}:${UI_PORT:-3000}
|
||||
|
||||
postgres:
|
||||
image: postgres:16.3-alpine
|
||||
hostname: "postgres-db"
|
||||
volumes:
|
||||
- ./_data/postgres:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_ADMIN_USER}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_ADMIN_PASSWORD}
|
||||
- POSTGRES_DB=${POSTGRES_DB}
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:${POSTGRES_PORT:-5432}"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "sh -c 'pg_isready -U ${POSTGRES_ADMIN_USER} -d ${POSTGRES_DB}'"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
valkey:
|
||||
image: valkey/valkey:7-alpine3.19
|
||||
hostname: "valkey"
|
||||
volumes:
|
||||
- ./_data/valkey:/data
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
ports:
|
||||
- "${VALKEY_PORT:-6379}:6379"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "sh -c 'valkey-cli ping'"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
worker:
|
||||
image: prowlercloud/prowler-api:${PROWLER_API_VERSION:-latest}
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
depends_on:
|
||||
valkey:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
entrypoint:
|
||||
- "/home/prowler/docker-entrypoint.sh"
|
||||
- "worker"
|
||||
|
||||
worker-beat:
|
||||
image: prowlercloud/prowler-api:${PROWLER_API_VERSION:-latest}
|
||||
env_file:
|
||||
- path: ./.env
|
||||
required: false
|
||||
depends_on:
|
||||
valkey:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
entrypoint:
|
||||
- "../docker-entrypoint.sh"
|
||||
- "beat"
|
||||
|
After Width: | Height: | Size: 463 KiB |
|
After Width: | Height: | Size: 239 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 599 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 273 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 518 KiB |
|
After Width: | Height: | Size: 258 KiB |
|
After Width: | Height: | Size: 192 KiB |
|
After Width: | Height: | Size: 268 KiB |
|
After Width: | Height: | Size: 328 KiB |
|
After Width: | Height: | Size: 512 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 163 KiB |
|
After Width: | Height: | Size: 277 KiB |
@@ -1,6 +1,14 @@
|
||||
**Prowler** is an Open Source security tool to perform AWS, Azure, Google Cloud and Kubernetes security best practices assessments, audits, incident response, continuous monitoring, hardening and forensics readiness, and also remediations! We have Prowler CLI (Command Line Interface) that we call Prowler Open Source and a service on top of it that we call <a href="https://prowler.com">Prowler SaaS</a>.
|
||||
|
||||
## Prowler CLI
|
||||
## Prowler App
|
||||
|
||||

|
||||
|
||||
Prowler App is a web application that allows you to run Prowler in a simple way. It provides a user-friendly interface to configure and run scans, view results, and manage your security findings.
|
||||
|
||||
See how to install the Prowler App in the [Quick Start](#prowler-app-installation) section.
|
||||
|
||||
## Prowler CLI
|
||||
|
||||
```console
|
||||
prowler <provider>
|
||||
@@ -17,7 +25,95 @@ prowler dashboard
|
||||
It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, FedRAMP, PCI-DSS, GDPR, HIPAA, FFIEC, SOC2, GXP, AWS Well-Architected Framework Security Pillar, AWS Foundational Technical Review (FTR), ENS (Spanish National Security Scheme) and your custom security frameworks.
|
||||
|
||||
## Quick Start
|
||||
### Installation
|
||||
### Prowler App Installation
|
||||
|
||||
Prowler App can be installed in different ways, depending on your environment:
|
||||
|
||||
> See how to use Prowler App in the [Prowler App](tutorials/prowler-app.md) section.
|
||||
|
||||
=== "Docker Compose"
|
||||
|
||||
_Requirements_:
|
||||
|
||||
* `Docker Compose` installed: https://docs.docker.com/compose/install/.
|
||||
|
||||
_Commands_:
|
||||
|
||||
``` bash
|
||||
curl -LO https://raw.githubusercontent.com/prowler-cloud/prowler/refs/heads/master/docker-compose.yml
|
||||
curl -LO https://raw.githubusercontent.com/prowler-cloud/prowler/refs/heads/master/.env
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
> Enjoy Prowler App at http://localhost:3000 by signing up with your email and password.
|
||||
|
||||
???+ note
|
||||
You can change the environment variables in the `.env` file. Note that it is not recommended to use the default values in production environments.
|
||||
|
||||
???+ note
|
||||
There is a development mode available, you can use the file https://github.com/prowler-cloud/prowler/blob/master/docker-compose.dev.yml to run the app in development mode.
|
||||
|
||||
???+ warning
|
||||
Google and GitHub authentication is only available in [Prowler Cloud](https://prowler.com).
|
||||
|
||||
=== "GitHub"
|
||||
|
||||
_Requirements_:
|
||||
|
||||
* `git` installed.
|
||||
* `poetry` installed: [poetry installation](https://python-poetry.org/docs/#installation).
|
||||
* `npm` installed: [npm installation](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).
|
||||
* `Docker Compose` installed: https://docs.docker.com/compose/install/.
|
||||
|
||||
_Commands to run the API_:
|
||||
|
||||
``` bash
|
||||
git clone https://github.com/prowler-cloud/prowler \
|
||||
cd prowler/api \
|
||||
poetry install \
|
||||
poetry shell \
|
||||
set -a \
|
||||
source .env \
|
||||
docker compose up postgres valkey -d \
|
||||
cd src/backend \
|
||||
python manage.py migrate --database admin \
|
||||
gunicorn -c config/guniconf.py config.wsgi:application
|
||||
```
|
||||
|
||||
> Now, you can access the API documentation at http://localhost:8080/api/v1/docs.
|
||||
|
||||
_Commands to run the API Worker_:
|
||||
|
||||
``` bash
|
||||
git clone https://github.com/prowler-cloud/prowler \
|
||||
cd prowler/api \
|
||||
poetry install \
|
||||
poetry shell \
|
||||
set -a \
|
||||
source .env \
|
||||
cd src/backend \
|
||||
python -m celery -A config.celery worker -l info -E
|
||||
```
|
||||
|
||||
_Commands to run the UI_:
|
||||
|
||||
``` bash
|
||||
git clone https://github.com/prowler-cloud/prowler \
|
||||
cd prowler/ui \
|
||||
npm install \
|
||||
npm run build \
|
||||
npm start
|
||||
```
|
||||
|
||||
> Enjoy Prowler App at http://localhost:3000 by signing up with your email and password.
|
||||
|
||||
???+ warning
|
||||
Make sure to have `api/.env` and `ui/.env.local` files with the required environment variables. You can find the required environment variables in the [`api/.env.template`](https://github.com/prowler-cloud/prowler/blob/master/api/.env.example) and [`ui/.env.template`](https://github.com/prowler-cloud/prowler/blob/master/ui/.env.template) files.
|
||||
|
||||
???+ warning
|
||||
Google and GitHub authentication is only available in [Prowler Cloud](https://prowler.com).
|
||||
|
||||
### Prowler CLI Installation
|
||||
|
||||
Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/), thus can be installed as Python package with `Python >= 3.9`:
|
||||
|
||||
@@ -195,18 +291,23 @@ Prowler is available as a project in [PyPI](https://pypi.org/project/prowler/),
|
||||
|
||||
## Prowler container versions
|
||||
|
||||
The available versions of Prowler are the following:
|
||||
The available versions of Prowler CLI are the following:
|
||||
|
||||
- `latest`: in sync with `master` branch (bear in mind that it is not a stable version)
|
||||
- `v4-latest`: in sync with `v4` branch (bear in mind that it is not a stable version)
|
||||
- `v3-latest`: in sync with `v3` branch (bear in mind that it is not a stable version)
|
||||
- `<x.y.z>` (release): you can find the releases [here](https://github.com/prowler-cloud/prowler/releases), those are stable releases.
|
||||
- `stable`: this tag always point to the latest release.
|
||||
- `v4-stable`: this tag always point to the latest release for v4.
|
||||
- `v3-stable`: this tag always point to the latest release for v3.
|
||||
|
||||
The container images are available here:
|
||||
|
||||
- [DockerHub](https://hub.docker.com/r/toniblyx/prowler/tags)
|
||||
- [AWS Public ECR](https://gallery.ecr.aws/prowler-cloud/prowler)
|
||||
- Prowler CLI:
|
||||
- [DockerHub](https://hub.docker.com/r/toniblyx/prowler/tags)
|
||||
- [AWS Public ECR](https://gallery.ecr.aws/prowler-cloud/prowler)
|
||||
- Prowler App:
|
||||
- [DockerHub - Prowler UI](https://hub.docker.com/r/prowlercloud/prowler-ui/tags)
|
||||
- [DockerHub - Prowler API](https://hub.docker.com/r/prowlercloud/prowler-api/tags)
|
||||
|
||||
## High level architecture
|
||||
|
||||
@@ -214,6 +315,22 @@ You can run Prowler from your workstation, a Kubernetes Job, a Google Compute En
|
||||
|
||||

|
||||
|
||||
### Prowler App
|
||||
|
||||
The **Prowler App** consists of three main components:
|
||||
|
||||
- **Prowler UI**: A user-friendly web interface for running Prowler and viewing results, powered by Next.js.
|
||||
- **Prowler API**: The backend API that executes Prowler scans and stores the results, built with Django REST Framework.
|
||||
- **Prowler SDK**: A Python SDK that integrates with the Prowler CLI for advanced functionality.
|
||||
|
||||
The app leverages the following supporting infrastructure:
|
||||
|
||||
- **PostgreSQL**: Used for persistent storage of scan results.
|
||||
- **Celery Workers**: Facilitate asynchronous execution of Prowler scans.
|
||||
- **Valkey**: An in-memory database serving as a message broker for the Celery workers.
|
||||
|
||||

|
||||
|
||||
## Deprecations from v3
|
||||
|
||||
### General
|
||||
@@ -231,6 +348,40 @@ We have deprecated some of our outputs formats:
|
||||
- To send only FAILS to AWS Security Hub, now use either `--send-sh-only-fails` or `--security-hub --status FAIL`.
|
||||
|
||||
## Basic Usage
|
||||
### Prowler App
|
||||
|
||||
#### **Access the App**
|
||||
Go to [http://localhost:3000](http://localhost:3000) after installing the app (see [Quick Start](#prowler-app-installation)). Sign up with your email and password.
|
||||
|
||||
<img src="img/sign-up-button.png" alt="Sign Up Button" width="320"/>
|
||||
<img src="img/sign-up.png" alt="Sign Up" width="285"/>
|
||||
|
||||
#### **Log In**
|
||||
Log in with your email and password to start using the Prowler App.
|
||||
|
||||
<img src="img/log-in.png" alt="Log In" width="285"/>
|
||||
|
||||
#### **Add a Provider**
|
||||
- Go to `Settings > Cloud Providers` and click `Add Account`.
|
||||
- Select the provider you want to scan (AWS, GCP, Azure, Kubernetes).
|
||||
- Enter the provider's ID (AWS Account ID, GCP Project ID, Azure Subscription ID, Kubernetes Cluster) and optional alias.
|
||||
- Follow the instructions to add your credentials.
|
||||
|
||||
#### **Start a Scan**
|
||||
After successfully adding and testing your credentials, Prowler will start scanning your cloud environment, click on the `Go to Scans` button to see the progress.
|
||||
|
||||
#### **View Results**
|
||||
While the scan is running, start exploring the findings in these sections:
|
||||
|
||||
- **Overview**: High-level summary of the scans. <img src="../../img/overview.png" alt="Overview" width="700"/>
|
||||
- **Compliance**: Insights into compliance status. <img src="../../img/compliance.png" alt="Compliance" width="700"/>
|
||||
|
||||
> See more details about the Prowler App usage in the [Prowler App](tutorials/prowler-app.md) section.
|
||||
|
||||
???+ note
|
||||
Prowler will automatically scan all configured providers every **24 hours**, ensuring your cloud environment stays continuously monitored.
|
||||
|
||||
### Prowler CLI
|
||||
|
||||
To run Prowler, you will need to specify the provider (e.g `aws`, `gcp`, `azure` or `kubernetes`):
|
||||
|
||||
@@ -289,7 +440,7 @@ You can always use `-h`/`--help` to access to the usage information and all the
|
||||
prowler --help
|
||||
```
|
||||
|
||||
### AWS
|
||||
#### AWS
|
||||
|
||||
Use a custom AWS profile with `-p`/`--profile` and/or AWS regions which you want to audit with `-f`/`--filter-region`:
|
||||
|
||||
@@ -302,7 +453,7 @@ prowler aws --profile custom-profile -f us-east-1 eu-south-2
|
||||
|
||||
See more details about AWS Authentication in [Requirements](getting-started/requirements.md#aws)
|
||||
|
||||
### Azure
|
||||
#### Azure
|
||||
|
||||
With Azure you need to specify which auth method is going to be used:
|
||||
|
||||
@@ -327,7 +478,7 @@ Prowler by default scans all the subscriptions that is allowed to scan, if you w
|
||||
prowler azure --az-cli-auth --subscription-ids <subscription ID 1> <subscription ID 2> ... <subscription ID N>
|
||||
```
|
||||
|
||||
### Google Cloud
|
||||
#### Google Cloud
|
||||
|
||||
Prowler will use by default your User Account credentials, you can configure it using:
|
||||
|
||||
@@ -349,7 +500,7 @@ prowler gcp --project-ids <Project ID 1> <Project ID 2> ... <Project ID N>
|
||||
|
||||
See more details about GCP Authentication in [Requirements](getting-started/requirements.md#google-cloud)
|
||||
|
||||
### Kubernetes
|
||||
#### Kubernetes
|
||||
|
||||
Prowler allows you to scan your Kubernetes Cluster either from within the cluster or from outside the cluster.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ To allow Prowler assume an identity to start the scan with the required privileg
|
||||
7. Fill the "Description" and "Expires" fields and click on "Add"
|
||||
8. Copy the value of the secret, it is going to be used as `AZURE_CLIENT_SECRET` environment variable.
|
||||
|
||||

|
||||

|
||||
|
||||
## Assigning the proper permissions
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
# Prowler App
|
||||
|
||||
The **Prowler App** is a user-friendly interface for the Prowler CLI, providing a visual dashboard to monitor your cloud security posture. This tutorial will guide you through setting up and using the Prowler App.
|
||||
|
||||
After [installing](../index.md#prowler-app-installation) the **Prowler App**, access it at [http://localhost:3000](http://localhost:3000).
|
||||
You can also access to the auto-generated **Prowler API** documentation at [http://localhost:8080/api/v1/docs](http://localhost:8080/api/v1/docs) to see all the available endpoints, parameters and responses.
|
||||
|
||||
## **Step 1: Sign Up**
|
||||
To get started, sign up using your email and password:
|
||||
|
||||
<img src="../../img/sign-up-button.png" alt="Sign Up Button" width="320"/>
|
||||
<img src="../../img/sign-up.png" alt="Sign Up" width="285"/>
|
||||
|
||||
---
|
||||
|
||||
## **Step 2: Log In**
|
||||
Once you’ve signed up, log in with your email and password to start using the Prowler App.
|
||||
|
||||
<img src="../../img/log-in.png" alt="Log In" width="350"/>
|
||||
|
||||
You will see the Overview page with no data yet, so let's start adding a provider to scan your cloud environment.
|
||||
|
||||
---
|
||||
|
||||
## **Step 3: Add a Provider**
|
||||
To run your first scan, you need to add a cloud provider account. Prowler App supports AWS, Azure, GCP, and Kubernetes.
|
||||
|
||||
1. Navigate to `Settings > Cloud Providers`.
|
||||
2. Click `Add Account` to set up a new provider and provide your credentials:
|
||||
|
||||
<img src="../../img/add-provider.png" alt="Add Provider" width="700"/>
|
||||
|
||||
---
|
||||
|
||||
## **Step 4: Configure the Provider**
|
||||
Choose the provider you want to scan from the following options:
|
||||
|
||||
<img src="../../img/select-provider.png" alt="Select a Provider" width="700"/>
|
||||
|
||||
Once you’ve selected a provider, you need to provide the Provider UID:
|
||||
|
||||
- **AWS**: Enter your AWS Account ID.
|
||||
- **GCP**: Enter your GCP Project ID.
|
||||
- **Azure**: Enter your Azure Subscription ID.
|
||||
- **Kubernetes**: Enter your Kubernetes Cluster name.
|
||||
|
||||
Optionally, provide a **Provider Alias** for easier identification. Follow the instructions provided to add your credentials:
|
||||
|
||||
---
|
||||
### **Step 4.1: AWS Credentials**
|
||||
For AWS, enter your `AWS Account ID` and choose one of the following methods to connect:
|
||||
|
||||
#### **Step 4.1.1: IAM Access Keys**
|
||||
1. Select `Connect via Credentials`.
|
||||
|
||||
<img src="../../img/connect-aws-credentials.png" alt="AWS Credentials" width="350"/>
|
||||
|
||||
2. Enter your `Access Key ID`, `Secret Access Key` and optionally a `Session Token`:
|
||||
|
||||
<img src="../../img/aws-credentials.png" alt="AWS Credentials" width="350"/>
|
||||
|
||||
#### **Step 4.1.2: IAM Role**
|
||||
1. Select `Connect assuming IAM Role`.
|
||||
|
||||
<img src="../../img/connect-aws-role.png" alt="AWS Role" width="350"/>
|
||||
|
||||
2. Enter the `Role ARN` and any optional field like the AWS Access Keys to assume the role, the `External ID`, the `Role Session Name` or the `Session Duration`:
|
||||
|
||||
<img src="../../img/aws-role.png" alt="AWS Role" width="700"/>
|
||||
|
||||
---
|
||||
|
||||
### **Step 4.2: Azure Credentials**
|
||||
For Azure, Prowler App uses a Service Principal to authenticate. See the steps in https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/azure/create-prowler-service-principal/ to create a Service Principal. Then, enter the `Tenant ID`, `Client ID` and `Client Secret` of the Service Principal.
|
||||
|
||||
<img src="../../img/azure-credentials.png" alt="Azure Credentials" width="700"/>
|
||||
|
||||
---
|
||||
### **Step 4.3: GCP Credentials**
|
||||
To connect your GCP Project, you need to use the Application Default Credentials (ADC) returned by the `gcloud` CLI. Here’s how to set up:
|
||||
|
||||
1. Run the following command in your terminal to authenticate with GCP:
|
||||
```bash
|
||||
gcloud auth application-default login
|
||||
```
|
||||
2. Once authenticated, get the `Client ID`, `Client Secret` and `Refresh Token` from `~/.config/gcloud/application_default_credentials`.
|
||||
3. Paste the `Client ID`, `Client Secret` and `Refresh Token` into the Prowler App.
|
||||
|
||||
<img src="../../img/gcp-credentials.png" alt="GCP Credentials" width="700"/>
|
||||
|
||||
---
|
||||
### **Step 4.4: Kubernetes Credentials**
|
||||
For Kubernetes, Prowler App uses a `kubeconfig` file to authenticate, paste the contents of your `kubeconfig` file into the `Kubeconfig content` field.
|
||||
|
||||
By default, the `kubeconfig` file is located at `~/.kube/config`.
|
||||
|
||||
<img src="../../img/kubernetes-credentials.png" alt="Kubernetes Credentials" width="700"/>
|
||||
|
||||
---
|
||||
|
||||
## **Step 5: Test Connection**
|
||||
After adding your credentials of your cloud account, click the `Launch` button to verify that the Prowler App can successfully connect to your provider:
|
||||
|
||||
<img src="../../img/test-connection-button.png" alt="Test Connection" width="700"/>
|
||||
|
||||
|
||||
## **Step 6: Scan started**
|
||||
After successfully adding and testing your credentials, Prowler will start scanning your cloud environment, click on the `Go to Scans` button to see the progress:
|
||||
|
||||
<img src="../../img/provider-added.png" alt="Start Now" width="700"/>
|
||||
|
||||
???+ note
|
||||
Prowler will automatically scan all configured providers every **24 hours**, ensuring your cloud environment stays continuously monitored.
|
||||
---
|
||||
|
||||
## **Step 7: Monitor Scan Progress**
|
||||
Track the progress of your scan in the `Scans` section:
|
||||
|
||||
<img src="../../img/scan-progress.png" alt="Scan Progress" width="700"/>
|
||||
|
||||
---
|
||||
|
||||
## **Step 8: Analyze the Findings**
|
||||
While the scan is running, start exploring the findings in these sections:
|
||||
|
||||
- **Overview**: High-level summary of the scans. <img src="../../img/overview.png" alt="Overview" width="700"/>
|
||||
- **Compliance**: Insights into compliance status. <img src="../../img/compliance.png" alt="Compliance" width="700"/>
|
||||
- **Issues**: Types of issues detected.
|
||||
|
||||
<img src="../../img/issues.png" alt="Issues" width="300" style="text-align: center;"/>
|
||||
|
||||
- **Browse All Findings**: Detailed list of findings detected, where you can filter by severity, service, and more. <img src="../../img/findings.png" alt="Findings" width="700"/>
|
||||
@@ -11,6 +11,7 @@ theme:
|
||||
name: material
|
||||
favicon: favicon.ico
|
||||
features:
|
||||
- content.code.copy
|
||||
- navigation.tabs
|
||||
- navigation.tabs.sticky
|
||||
- navigation.sections
|
||||
@@ -49,6 +50,7 @@ nav:
|
||||
- Overview: index.md
|
||||
- Requirements: getting-started/requirements.md
|
||||
- Tutorials:
|
||||
- Prowler App: tutorials/prowler-app.md
|
||||
- Miscellaneous: tutorials/misc.md
|
||||
- Reporting: tutorials/reporting.md
|
||||
- Compliance: tutorials/compliance.md
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"appstream:Describe*",
|
||||
"appstream:List*",
|
||||
"backup:List*",
|
||||
"backup:Get*",
|
||||
"bedrock:List*",
|
||||
"bedrock:Get*",
|
||||
"cloudtrail:GetInsightSelectors",
|
||||
@@ -29,6 +30,7 @@
|
||||
"glue:GetConnections",
|
||||
"glue:GetSecurityConfiguration*",
|
||||
"glue:SearchTables",
|
||||
"glue:GetMLTransforms",
|
||||
"lambda:GetFunction*",
|
||||
"logs:FilterLogEvents",
|
||||
"lightsail:GetRelationalDatabases",
|
||||
|
||||
@@ -680,13 +680,13 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "bandit"
|
||||
version = "1.7.10"
|
||||
version = "1.8.0"
|
||||
description = "Security oriented static analyser for python code."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "bandit-1.7.10-py3-none-any.whl", hash = "sha256:665721d7bebbb4485a339c55161ac0eedde27d51e638000d91c8c2d68343ad02"},
|
||||
{file = "bandit-1.7.10.tar.gz", hash = "sha256:59ed5caf5d92b6ada4bf65bc6437feea4a9da1093384445fed4d472acc6cff7b"},
|
||||
{file = "bandit-1.8.0-py3-none-any.whl", hash = "sha256:b1a61d829c0968aed625381e426aa378904b996529d048f8d908fa28f6b13e38"},
|
||||
{file = "bandit-1.8.0.tar.gz", hash = "sha256:b5bfe55a095abd9fe20099178a7c6c060f844bfd4fe4c76d28e35e4c52b9d31e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -761,17 +761,17 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "boto3"
|
||||
version = "1.35.66"
|
||||
version = "1.35.71"
|
||||
description = "The AWS SDK for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "boto3-1.35.66-py3-none-any.whl", hash = "sha256:09a610f8cf4d3c22d4ca69c1f89079e3a1c82805ce94fa0eb4ecdd4d2ba6c4bc"},
|
||||
{file = "boto3-1.35.66.tar.gz", hash = "sha256:c392b9168b65e9c23483eaccb5b68d1f960232d7f967a1e00a045ba065ce050d"},
|
||||
{file = "boto3-1.35.71-py3-none-any.whl", hash = "sha256:e2969a246bb3208122b3c349c49cc6604c6fc3fc2b2f65d99d3e8ccd745b0c16"},
|
||||
{file = "boto3-1.35.71.tar.gz", hash = "sha256:3ed7172b3d4fceb6218bb0ec3668c4d40c03690939c2fca4f22bb875d741a07f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
botocore = ">=1.35.66,<1.36.0"
|
||||
botocore = ">=1.35.71,<1.36.0"
|
||||
jmespath = ">=0.7.1,<2.0.0"
|
||||
s3transfer = ">=0.10.0,<0.11.0"
|
||||
|
||||
@@ -780,13 +780,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
|
||||
|
||||
[[package]]
|
||||
name = "botocore"
|
||||
version = "1.35.70"
|
||||
version = "1.35.71"
|
||||
description = "Low-level, data-driven core of boto 3."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "botocore-1.35.70-py3-none-any.whl", hash = "sha256:ba8a4797cf7c5d9c237e67a62692f5146e895613fd3e6a43b00b66f3a8c7fc73"},
|
||||
{file = "botocore-1.35.70.tar.gz", hash = "sha256:18d1bb505722d9efd50c50719ed8de7284bfe6d3908a9e08756a7646e549da21"},
|
||||
{file = "botocore-1.35.71-py3-none-any.whl", hash = "sha256:fc46e7ab1df3cef66dfba1633f4da77c75e07365b36f03bd64a3793634be8fc1"},
|
||||
{file = "botocore-1.35.71.tar.gz", hash = "sha256:f9fa058e0393660c3fe53c1e044751beb64b586def0bd2212448a7c328b0cbba"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -5290,4 +5290,4 @@ type = ["pytest-mypy"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9,<3.13"
|
||||
content-hash = "3e674bb39019458d469da43301c0ee7607277bd38ab294b6190935882eb280b8"
|
||||
content-hash = "c6497888031fcb8ddece4848532980baa412943e90fa8298dd0e9671fbfb5391"
|
||||
@@ -556,19 +556,6 @@ def execute_checks(
|
||||
bar()
|
||||
bar.title = f"-> {Fore.GREEN}Scan completed!{Style.RESET_ALL}"
|
||||
|
||||
# Custom report interface
|
||||
if os.environ.get("PROWLER_REPORT_LIB_PATH"):
|
||||
try:
|
||||
logger.info("Using custom report interface ...")
|
||||
lib = os.environ["PROWLER_REPORT_LIB_PATH"]
|
||||
outputs_module = importlib.import_module(lib)
|
||||
custom_report_interface = getattr(outputs_module, "report")
|
||||
|
||||
# TODO: review this call and see if we can remove the global_provider.output_options since it is contained in the global_provider
|
||||
custom_report_interface(check_findings, output_options, global_provider)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
|
||||
return all_findings
|
||||
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ def load_checks_to_execute(
|
||||
):
|
||||
checks_to_execute.add(check_name)
|
||||
# Only execute threat detection checks if threat-detection category is set
|
||||
if categories and categories != [] and "threat-detection" not in categories:
|
||||
if not categories or "threat-detection" not in categories:
|
||||
for threat_detection_check in check_categories.get("threat-detection", []):
|
||||
checks_to_execute.discard(threat_detection_check)
|
||||
|
||||
|
||||
@@ -322,8 +322,9 @@ class CheckMetadata(BaseModel):
|
||||
checks = set()
|
||||
|
||||
if service:
|
||||
if service == "lambda":
|
||||
service = "awslambda"
|
||||
# This is a special case for the AWS provider since `lambda` is a reserved keyword in Python
|
||||
if service == "awslambda":
|
||||
service = "lambda"
|
||||
checks = {
|
||||
check_name
|
||||
for check_name, check_metadata in bulk_checks_metadata.items()
|
||||
|
||||
@@ -9265,10 +9265,7 @@
|
||||
"us-west-2"
|
||||
],
|
||||
"aws-cn": [],
|
||||
"aws-us-gov": [
|
||||
"us-gov-east-1",
|
||||
"us-gov-west-1"
|
||||
]
|
||||
"aws-us-gov": []
|
||||
}
|
||||
},
|
||||
"sagemaker-runtime": {
|
||||
|
||||
@@ -446,7 +446,7 @@ class RDS(AWSService):
|
||||
arn=arn,
|
||||
sns_topic_arn=event["SnsTopicArn"],
|
||||
status=event["Status"],
|
||||
source_type=event["SourceType"],
|
||||
source_type=event.get("SourceType", ""),
|
||||
source_id=event.get("SourceIdsList", []),
|
||||
event_list=event.get("EventCategoriesList", []),
|
||||
enabled=event["Enabled"],
|
||||
|
||||
@@ -18,12 +18,7 @@ class containerregistry_not_publicly_accessible(Check):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Container Registry {container_registry_info.name} from subscription {subscription} allows unrestricted network access."
|
||||
|
||||
if (
|
||||
getattr(
|
||||
container_registry_info.network_rule_set, "default_action", ""
|
||||
).lower()
|
||||
== "deny"
|
||||
):
|
||||
if not container_registry_info.public_network_access:
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Container Registry {container_registry_info.name} from subscription {subscription} does not allow unrestricted network access."
|
||||
|
||||
|
||||
@@ -37,8 +37,13 @@ class ContainerRegistry(AzureService):
|
||||
resource_group=resource_group,
|
||||
sku=getattr(registry.sku, "name", ""),
|
||||
login_server=getattr(registry, "login_server", ""),
|
||||
public_network_access=getattr(
|
||||
registry, "public_network_access", ""
|
||||
public_network_access=(
|
||||
False
|
||||
if getattr(
|
||||
registry, "public_network_access" "Enabled"
|
||||
)
|
||||
== "Disabled"
|
||||
else True
|
||||
),
|
||||
admin_user_enabled=getattr(
|
||||
registry, "admin_user_enabled", False
|
||||
@@ -93,7 +98,7 @@ class ContainerRegistryInfo:
|
||||
resource_group: str
|
||||
sku: str
|
||||
login_server: str
|
||||
public_network_access: str
|
||||
public_network_access: bool
|
||||
admin_user_enabled: bool
|
||||
network_rule_set: NetworkRuleSet
|
||||
monitor_diagnostic_settings: list[DiagnosticSetting]
|
||||
|
||||
@@ -55,7 +55,9 @@ class GCPService:
|
||||
project_ids = []
|
||||
for project_id in audited_project_ids:
|
||||
try:
|
||||
client = discovery.build("serviceusage", "v1")
|
||||
client = discovery.build(
|
||||
"serviceusage", "v1", credentials=self.credentials
|
||||
)
|
||||
request = client.services().get(
|
||||
name=f"projects/{project_id}/services/{self.service}.googleapis.com"
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
from colorama import Fore, Style
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
@@ -74,14 +75,14 @@ class KubernetesProvider(Provider):
|
||||
fixer_config: dict = {},
|
||||
mutelist_path: str = None,
|
||||
mutelist_content: dict = {},
|
||||
kubeconfig_content: dict = None,
|
||||
kubeconfig_content: Union[dict, str] = None,
|
||||
):
|
||||
"""
|
||||
Initializes the KubernetesProvider instance.
|
||||
|
||||
Args:
|
||||
kubeconfig_file (str): Path to the kubeconfig file.
|
||||
kubeconfig_content (dict): Content of the kubeconfig file.
|
||||
kubeconfig_content (str or dict): Content of the kubeconfig file.
|
||||
context (str): Context name.
|
||||
namespace (list): List of namespaces.
|
||||
config_content (dict): Audit configuration.
|
||||
@@ -224,7 +225,7 @@ class KubernetesProvider(Provider):
|
||||
@staticmethod
|
||||
def setup_session(
|
||||
kubeconfig_file: str = None,
|
||||
kubeconfig_content: dict = None,
|
||||
kubeconfig_content: Union[dict, str] = None,
|
||||
context: str = None,
|
||||
) -> KubernetesSession:
|
||||
"""
|
||||
@@ -232,7 +233,7 @@ class KubernetesProvider(Provider):
|
||||
|
||||
Args:
|
||||
kubeconfig_file (str): Path to the kubeconfig file.
|
||||
kubeconfig_content (dict): Content of the kubeconfig file.
|
||||
kubeconfig_content (str or dict): Content of the kubeconfig file.
|
||||
context (str): Context name.
|
||||
|
||||
Returns:
|
||||
@@ -243,14 +244,20 @@ class KubernetesProvider(Provider):
|
||||
KubernetesInvalidProviderIdError: If the provider ID is invalid.
|
||||
KubernetesSetUpSessionError: If an error occurs while setting up the session.
|
||||
"""
|
||||
logger.info(f"Using kubeconfig file: {kubeconfig_file}")
|
||||
try:
|
||||
if kubeconfig_content:
|
||||
config.load_kube_config_from_dict(
|
||||
safe_load(kubeconfig_content), context=context
|
||||
)
|
||||
|
||||
logger.info("Using kubeconfig content...")
|
||||
config_data = safe_load(kubeconfig_content)
|
||||
config.load_kube_config_from_dict(config_data, context=context)
|
||||
if context:
|
||||
contexts = config_data.get("contexts", [])
|
||||
for context_item in contexts:
|
||||
if context_item["name"] == context:
|
||||
context = context_item
|
||||
else:
|
||||
context = config_data.get("contexts", [])[0]
|
||||
else:
|
||||
logger.info(f"Using kubeconfig file: {kubeconfig_file}...")
|
||||
kubeconfig_file = (
|
||||
kubeconfig_file if kubeconfig_file else "~/.kube/config"
|
||||
)
|
||||
@@ -273,17 +280,19 @@ class KubernetesProvider(Provider):
|
||||
return KubernetesSession(
|
||||
api_client=client.ApiClient(), context=context
|
||||
)
|
||||
if context:
|
||||
contexts = config.list_kube_config_contexts(
|
||||
config_file=kubeconfig_file
|
||||
)[0]
|
||||
for context_item in contexts:
|
||||
if context_item["name"] == context:
|
||||
context = context_item
|
||||
else:
|
||||
context = config.list_kube_config_contexts(config_file=kubeconfig_file)[
|
||||
1
|
||||
]
|
||||
if context:
|
||||
contexts = config.list_kube_config_contexts(
|
||||
config_file=kubeconfig_file
|
||||
)[0]
|
||||
for context_item in contexts:
|
||||
if context_item["name"] == context:
|
||||
context = context_item
|
||||
else:
|
||||
# If no context is provided, use the active context in the kubeconfig file
|
||||
# The first element is the list of contexts, the second is the active context
|
||||
context = config.list_kube_config_contexts(
|
||||
config_file=kubeconfig_file
|
||||
)[1]
|
||||
return KubernetesSession(api_client=client.ApiClient(), context=context)
|
||||
|
||||
except parser.ParserError as parser_error:
|
||||
@@ -318,7 +327,7 @@ class KubernetesProvider(Provider):
|
||||
@staticmethod
|
||||
def test_connection(
|
||||
kubeconfig_file: str = "~/.kube/config",
|
||||
kubeconfig_content: dict = None,
|
||||
kubeconfig_content: Union[dict, str] = None,
|
||||
namespace: str = None,
|
||||
provider_id: str = None,
|
||||
raise_on_exception: bool = True,
|
||||
@@ -328,7 +337,7 @@ class KubernetesProvider(Provider):
|
||||
|
||||
Args:
|
||||
kubeconfig_file (str): Path to the kubeconfig file.
|
||||
kubeconfig_content (dict): Content of the kubeconfig file.
|
||||
kubeconfig_content (str or dict): Content of the kubeconfig file.
|
||||
namespace (str): Namespace name.
|
||||
provider_id (str): Provider ID to use, in this case, the Kubernetes context.
|
||||
raise_on_exception (bool): Whether to raise an exception on error.
|
||||
@@ -352,7 +361,7 @@ class KubernetesProvider(Provider):
|
||||
... )
|
||||
- Using the kubeconfig content:
|
||||
>>> connection = KubernetesProvider.test_connection(
|
||||
... kubeconfig_content={"kubecofig": "content"},
|
||||
... kubeconfig_content="kubeconfig content",
|
||||
... namespace="default",
|
||||
... provider_id="my-context",
|
||||
... raise_on_exception=True,
|
||||
|
||||
@@ -48,8 +48,8 @@ azure-mgmt-storage = "21.2.1"
|
||||
azure-mgmt-subscription = "3.1.1"
|
||||
azure-mgmt-web = "7.3.1"
|
||||
azure-storage-blob = "12.24.0"
|
||||
boto3 = "1.35.66"
|
||||
botocore = "1.35.70"
|
||||
boto3 = "1.35.71"
|
||||
botocore = "1.35.71"
|
||||
colorama = "0.4.6"
|
||||
cryptography = "43.0.1"
|
||||
dash = "2.18.2"
|
||||
@@ -76,7 +76,7 @@ tabulate = "0.9.0"
|
||||
tzlocal = "5.2"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
bandit = "1.7.10"
|
||||
bandit = "1.8.0"
|
||||
black = "24.10.0"
|
||||
coverage = "7.6.8"
|
||||
docker = "7.1.0"
|
||||
|
||||
@@ -14,11 +14,13 @@ S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME_CUSTOM_ALIAS = (
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_SEVERITY = "medium"
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME_SERVICE = "s3"
|
||||
|
||||
CLOUDTRAIL_THREAT_DETECTION_ENUMERATION_NAME = "cloudtrail_threat_detection_enumeration"
|
||||
|
||||
|
||||
class TestCheckLoader:
|
||||
provider = "aws"
|
||||
|
||||
def get_custom_check_metadata(self):
|
||||
def get_custom_check_s3_metadata(self):
|
||||
return CheckMetadata(
|
||||
Provider="aws",
|
||||
CheckID=S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME,
|
||||
@@ -52,9 +54,37 @@ class TestCheckLoader:
|
||||
Compliance=[],
|
||||
)
|
||||
|
||||
def get_threat_detection_check_metadata(self):
|
||||
return CheckMetadata(
|
||||
Provider="aws",
|
||||
CheckID=CLOUDTRAIL_THREAT_DETECTION_ENUMERATION_NAME,
|
||||
CheckTitle="Ensure there are no potential enumeration threats in CloudTrail",
|
||||
CheckType=[],
|
||||
ServiceName="cloudtrail",
|
||||
SubServiceName="",
|
||||
ResourceIdTemplate="arn:partition:service:region:account-id:resource-id",
|
||||
Severity="critical",
|
||||
ResourceType="AwsCloudTrailTrail",
|
||||
Description="This check ensures that there are no potential enumeration threats in CloudTrail.",
|
||||
Risk="Potential enumeration threats in CloudTrail can lead to unauthorized access to resources.",
|
||||
RelatedUrl="",
|
||||
Remediation=Remediation(
|
||||
Code=Code(CLI="", NativeIaC="", Other="", Terraform=""),
|
||||
Recommendation=Recommendation(
|
||||
Text="To remediate this issue, ensure that there are no potential enumeration threats in CloudTrail.",
|
||||
Url="https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html#cloudtrail-concepts-logging-data-events",
|
||||
),
|
||||
),
|
||||
Categories=["threat-detection"],
|
||||
DependsOn=[],
|
||||
RelatedTo=[],
|
||||
Notes="",
|
||||
Compliance=[],
|
||||
)
|
||||
|
||||
def test_load_checks_to_execute(self):
|
||||
bulk_checks_metatada = {
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
|
||||
}
|
||||
|
||||
assert {S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME} == load_checks_to_execute(
|
||||
@@ -64,7 +94,7 @@ class TestCheckLoader:
|
||||
|
||||
def test_load_checks_to_execute_with_check_list(self):
|
||||
bulk_checks_metatada = {
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
|
||||
}
|
||||
check_list = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME]
|
||||
|
||||
@@ -76,7 +106,7 @@ class TestCheckLoader:
|
||||
|
||||
def test_load_checks_to_execute_with_severities(self):
|
||||
bulk_checks_metatada = {
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
|
||||
}
|
||||
severities = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_SEVERITY]
|
||||
|
||||
@@ -88,7 +118,7 @@ class TestCheckLoader:
|
||||
|
||||
def test_load_checks_to_execute_with_severities_and_services(self):
|
||||
bulk_checks_metatada = {
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
|
||||
}
|
||||
service_list = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME_SERVICE]
|
||||
severities = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_SEVERITY]
|
||||
@@ -104,7 +134,7 @@ class TestCheckLoader:
|
||||
self,
|
||||
):
|
||||
bulk_checks_metatada = {
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
|
||||
}
|
||||
service_list = ["ec2"]
|
||||
severities = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_SEVERITY]
|
||||
@@ -120,7 +150,7 @@ class TestCheckLoader:
|
||||
self,
|
||||
):
|
||||
bulk_checks_metatada = {
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
|
||||
}
|
||||
checks_file = "path/to/test_file"
|
||||
with patch(
|
||||
@@ -137,7 +167,7 @@ class TestCheckLoader:
|
||||
self,
|
||||
):
|
||||
bulk_checks_metatada = {
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
|
||||
}
|
||||
service_list = [S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME_SERVICE]
|
||||
|
||||
@@ -178,7 +208,7 @@ class TestCheckLoader:
|
||||
self,
|
||||
):
|
||||
bulk_checks_metatada = {
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
|
||||
}
|
||||
categories = {"internet-exposed"}
|
||||
|
||||
@@ -190,7 +220,7 @@ class TestCheckLoader:
|
||||
|
||||
def test_load_checks_to_execute_no_bulk_checks_metadata(self):
|
||||
bulk_checks_metatada = {
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
|
||||
}
|
||||
with patch(
|
||||
"prowler.lib.check.checks_loader.CheckMetadata.get_bulk",
|
||||
@@ -221,7 +251,7 @@ class TestCheckLoader:
|
||||
compliance_frameworks = ["soc2_aws"]
|
||||
|
||||
bulk_checks_metatada = {
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_metadata()
|
||||
S3_BUCKET_LEVEL_PUBLIC_ACCESS_BLOCK_NAME: self.get_custom_check_s3_metadata()
|
||||
}
|
||||
with patch(
|
||||
"prowler.lib.check.checks_loader.CheckMetadata.get_bulk",
|
||||
@@ -248,3 +278,27 @@ class TestCheckLoader:
|
||||
assert {"check1_name", "check2_name"} == update_checks_to_execute_with_aliases(
|
||||
checks_to_execute, check_aliases
|
||||
)
|
||||
|
||||
def test_threat_detection_category(self):
|
||||
bulk_checks_metatada = {
|
||||
CLOUDTRAIL_THREAT_DETECTION_ENUMERATION_NAME: self.get_threat_detection_check_metadata()
|
||||
}
|
||||
categories = {"threat-detection"}
|
||||
|
||||
assert {CLOUDTRAIL_THREAT_DETECTION_ENUMERATION_NAME} == load_checks_to_execute(
|
||||
bulk_checks_metadata=bulk_checks_metatada,
|
||||
categories=categories,
|
||||
provider=self.provider,
|
||||
)
|
||||
|
||||
def test_discard_threat_detection_checks(self):
|
||||
bulk_checks_metatada = {
|
||||
CLOUDTRAIL_THREAT_DETECTION_ENUMERATION_NAME: self.get_threat_detection_check_metadata()
|
||||
}
|
||||
categories = {}
|
||||
|
||||
assert set() == load_checks_to_execute(
|
||||
bulk_checks_metadata=bulk_checks_metatada,
|
||||
categories=categories,
|
||||
provider=self.provider,
|
||||
)
|
||||
@@ -32,6 +32,35 @@ mock_metadata = CheckMetadata(
|
||||
Compliance=[],
|
||||
)
|
||||
|
||||
mock_metadata_lambda = CheckMetadata(
|
||||
Provider="aws",
|
||||
CheckID="awslambda_function_url_public",
|
||||
CheckTitle="Check 1",
|
||||
CheckType=["type1"],
|
||||
ServiceName="lambda",
|
||||
SubServiceName="subservice1",
|
||||
ResourceIdTemplate="template1",
|
||||
Severity="high",
|
||||
ResourceType="resource1",
|
||||
Description="Description 1",
|
||||
Risk="risk1",
|
||||
RelatedUrl="url1",
|
||||
Remediation={
|
||||
"Code": {
|
||||
"CLI": "cli1",
|
||||
"NativeIaC": "native1",
|
||||
"Other": "other1",
|
||||
"Terraform": "terraform1",
|
||||
},
|
||||
"Recommendation": {"Text": "text1", "Url": "url1"},
|
||||
},
|
||||
Categories=["categoryone"],
|
||||
DependsOn=["dependency1"],
|
||||
RelatedTo=["related1"],
|
||||
Notes="notes1",
|
||||
Compliance=[],
|
||||
)
|
||||
|
||||
|
||||
class TestCheckMetada:
|
||||
|
||||
@@ -188,6 +217,46 @@ class TestCheckMetada:
|
||||
# Assertions
|
||||
assert result == {"accessanalyzer_enabled"}
|
||||
|
||||
@mock.patch("prowler.lib.check.models.load_check_metadata")
|
||||
@mock.patch("prowler.lib.check.models.recover_checks_from_provider")
|
||||
def test_list_by_service_lambda(self, mock_recover_checks, mock_load_metadata):
|
||||
# Mock the return value of recover_checks_from_provider
|
||||
mock_recover_checks.return_value = [
|
||||
("awslambda_function_url_public", "/path/to/awslambda_function_url_public")
|
||||
]
|
||||
|
||||
# Mock the return value of load_check_metadata
|
||||
mock_load_metadata.return_value = mock_metadata_lambda
|
||||
|
||||
bulk_metadata = CheckMetadata.get_bulk(provider="aws")
|
||||
|
||||
result = CheckMetadata.list(
|
||||
bulk_checks_metadata=bulk_metadata, service="lambda"
|
||||
)
|
||||
|
||||
# Assertions
|
||||
assert result == {"awslambda_function_url_public"}
|
||||
|
||||
@mock.patch("prowler.lib.check.models.load_check_metadata")
|
||||
@mock.patch("prowler.lib.check.models.recover_checks_from_provider")
|
||||
def test_list_by_service_awslambda(self, mock_recover_checks, mock_load_metadata):
|
||||
# Mock the return value of recover_checks_from_provider
|
||||
mock_recover_checks.return_value = [
|
||||
("awslambda_function_url_public", "/path/to/awslambda_function_url_public")
|
||||
]
|
||||
|
||||
# Mock the return value of load_check_metadata
|
||||
mock_load_metadata.return_value = mock_metadata_lambda
|
||||
|
||||
bulk_metadata = CheckMetadata.get_bulk(provider="aws")
|
||||
|
||||
result = CheckMetadata.list(
|
||||
bulk_checks_metadata=bulk_metadata, service="awslambda"
|
||||
)
|
||||
|
||||
# Assertions
|
||||
assert result == {"awslambda_function_url_public"}
|
||||
|
||||
@mock.patch("prowler.lib.check.models.load_check_metadata")
|
||||
@mock.patch("prowler.lib.check.models.recover_checks_from_provider")
|
||||
def test_list_by_service_invalid(self, mock_recover_checks, mock_load_metadata):
|
||||
|
||||
@@ -57,7 +57,7 @@ class Test_containerregistry_not_publicly_accessible:
|
||||
resource_group="mock_resource_group",
|
||||
sku="Basic",
|
||||
login_server="mock_login_server.azurecr.io",
|
||||
public_network_access="Enabled",
|
||||
public_network_access=True,
|
||||
admin_user_enabled=True,
|
||||
network_rule_set=NetworkRuleSet(default_action="Allow"),
|
||||
private_endpoint_connections=[],
|
||||
@@ -131,7 +131,7 @@ class Test_containerregistry_not_publicly_accessible:
|
||||
resource_group="mock_resource_group",
|
||||
sku="Basic",
|
||||
login_server="mock_login_server.azurecr.io",
|
||||
public_network_access="Enabled",
|
||||
public_network_access=False,
|
||||
admin_user_enabled=False,
|
||||
network_rule_set=NetworkRuleSet(default_action="Deny"),
|
||||
private_endpoint_connections=[],
|
||||
|
||||
@@ -32,7 +32,7 @@ class TestContainerRegistryService:
|
||||
resource_group="mock_resource_group",
|
||||
sku="Basic",
|
||||
login_server="mock_login_server.azurecr.io",
|
||||
public_network_access="Enabled",
|
||||
public_network_access=False,
|
||||
admin_user_enabled=True,
|
||||
network_rule_set=None,
|
||||
private_endpoint_connections=[],
|
||||
@@ -71,7 +71,7 @@ class TestContainerRegistryService:
|
||||
assert registry_info.resource_group == "mock_resource_group"
|
||||
assert registry_info.sku == "Basic"
|
||||
assert registry_info.login_server == "mock_login_server.azurecr.io"
|
||||
assert registry_info.public_network_access == "Enabled"
|
||||
assert not registry_info.public_network_access
|
||||
assert registry_info.admin_user_enabled is True
|
||||
assert isinstance(registry_info.monitor_diagnostic_settings, list)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import datetime
|
||||
from unittest import mock
|
||||
|
||||
from tests.providers.gcp.gcp_fixtures import (
|
||||
@@ -239,7 +240,10 @@ class Test_kms_key_rotation_enabled:
|
||||
project_id=GCP_PROJECT_ID,
|
||||
key_ring=keyring.name,
|
||||
location=keylocation.name,
|
||||
next_rotation_time="2025-09-01T00:00:00Z",
|
||||
# Next rotation time of now + 100 days
|
||||
next_rotation_time=(
|
||||
datetime.datetime.now() - datetime.timedelta(days=+100)
|
||||
).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
|
||||
members=["user:jane@example.com"],
|
||||
)
|
||||
]
|
||||
@@ -296,7 +300,10 @@ class Test_kms_key_rotation_enabled:
|
||||
project_id=GCP_PROJECT_ID,
|
||||
key_ring=keyring.name,
|
||||
location=keylocation.name,
|
||||
next_rotation_time="2024-09-01T00:00:00Z",
|
||||
# Next rotation time of now + 30 days
|
||||
next_rotation_time=(
|
||||
datetime.datetime.now() - datetime.timedelta(days=+30)
|
||||
).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
|
||||
members=["user:jane@example.com"],
|
||||
)
|
||||
]
|
||||
@@ -352,7 +359,10 @@ class Test_kms_key_rotation_enabled:
|
||||
id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1",
|
||||
project_id=GCP_PROJECT_ID,
|
||||
rotation_period="8776000s",
|
||||
next_rotation_time="2025-09-01T00:00:00Z",
|
||||
# Next rotation time of now + 100 days
|
||||
next_rotation_time=(
|
||||
datetime.datetime.now() - datetime.timedelta(days=+100)
|
||||
).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
|
||||
key_ring=keyring.name,
|
||||
location=keylocation.name,
|
||||
members=["user:jane@example.com"],
|
||||
@@ -412,7 +422,10 @@ class Test_kms_key_rotation_enabled:
|
||||
id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1",
|
||||
project_id=GCP_PROJECT_ID,
|
||||
rotation_period="8776000s",
|
||||
next_rotation_time="2024-09-01T00:00:00Z",
|
||||
# Next rotation time of now + 30 days
|
||||
next_rotation_time=(
|
||||
datetime.datetime.now() - datetime.timedelta(days=+30)
|
||||
).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
|
||||
key_ring=keyring.name,
|
||||
location=keylocation.name,
|
||||
members=["user:jane@example.com"],
|
||||
@@ -470,7 +483,10 @@ class Test_kms_key_rotation_enabled:
|
||||
id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1",
|
||||
project_id=GCP_PROJECT_ID,
|
||||
rotation_period="7776000s",
|
||||
next_rotation_time="2025-09-01T00:00:00Z",
|
||||
# Next rotation time of now + 100 days
|
||||
next_rotation_time=(
|
||||
datetime.datetime.now() - datetime.timedelta(days=+100)
|
||||
).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
|
||||
key_ring=keyring.name,
|
||||
location=keylocation.name,
|
||||
members=["user:jane@example.com"],
|
||||
@@ -530,7 +546,10 @@ class Test_kms_key_rotation_enabled:
|
||||
id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1",
|
||||
project_id=GCP_PROJECT_ID,
|
||||
rotation_period="7776000s",
|
||||
next_rotation_time="2024-09-01T00:00:00Z",
|
||||
# Next rotation time of now + 30 days
|
||||
next_rotation_time=(
|
||||
datetime.datetime.now() - datetime.timedelta(days=+30)
|
||||
).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
|
||||
key_ring=keyring.name,
|
||||
location=keylocation.name,
|
||||
members=["user:jane@example.com"],
|
||||
@@ -588,7 +607,10 @@ class Test_kms_key_rotation_enabled:
|
||||
id="projects/123/locations/us-central1/keyRings/keyring1/cryptoKeys/key1",
|
||||
project_id=GCP_PROJECT_ID,
|
||||
rotation_period="7776000s",
|
||||
next_rotation_time="2025-07-06T22:00:00.561275Z",
|
||||
# Next rotation time of now + 100 days
|
||||
next_rotation_time=(
|
||||
datetime.datetime.now() - datetime.timedelta(days=+100)
|
||||
).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
|
||||
key_ring=keyring.name,
|
||||
location=keylocation.name,
|
||||
members=["user:jane@example.com"],
|
||||
|
||||