mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-06-10 13:32:44 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 34728708c2 | |||
| a769e37615 | |||
| 9d2a8d9108 | |||
| e05519ff9f | |||
| 67b26072f8 | |||
| 2222082631 | |||
| 8b0cb4b981 | |||
| 9422eff8ab | |||
| e3c4368d32 | |||
| 2a641b39c8 | |||
| 02b713572b | |||
| 74251350bc | |||
| 8f745cdbe6 | |||
| 81226cd837 | |||
| a2824f7166 | |||
| edbbd86828 | |||
| c58dad2ca4 | |||
| b4befe3a10 | |||
| d98933c2e7 | |||
| 03dfa3816d | |||
| ad1261ce54 |
+8
-1
@@ -11,7 +11,14 @@ envs = "wt step copy-ignored"
|
||||
[[pre-start]]
|
||||
deps = "uv sync"
|
||||
|
||||
# Block 3: reminder - last visible output before `wt switch` returns.
|
||||
# Block 3: prepare pnpm via corepack.
|
||||
[[pre-start]]
|
||||
corepack-enable = "corepack enable"
|
||||
|
||||
[[pre-start]]
|
||||
corepack-install = "cd ui && corepack install"
|
||||
|
||||
# Block 4: reminder - last visible output before `wt switch` returns.
|
||||
# Hooks can't mutate the parent shell, so venv activation is manual.
|
||||
[[pre-start]]
|
||||
reminder = "echo '>> Reminder: activate the venv in this shell with: source .venv/bin/activate'"
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
name: 'Docs: Markdown Lint'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'v5.*'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'v5.*'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
markdown-lint:
|
||||
if: github.repository == 'prowler-cloud/prowler'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
|
||||
with:
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.github.com:443
|
||||
github.com:443
|
||||
registry.npmjs.org:443
|
||||
release-assets.githubusercontent.com:443
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: ui/.nvmrc
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
with:
|
||||
package_json_file: ui/package.json
|
||||
run_install: false
|
||||
|
||||
- name: Run markdownlint
|
||||
# Pin must match .pre-commit-config.yaml so prek and CI behave identically.
|
||||
# pnpm dlx doesn't accept --ignore-scripts as a flag; the env var
|
||||
# disables postinstall scripts on transitives the same way.
|
||||
env:
|
||||
pnpm_config_ignore_scripts: 'true'
|
||||
run: pnpm dlx markdownlint-cli@0.45.0 '**/*.md'
|
||||
@@ -541,6 +541,54 @@ jobs:
|
||||
flags: prowler-py${{ matrix.python-version }}-vercel
|
||||
files: ./vercel_coverage.xml
|
||||
|
||||
# Scaleway Provider
|
||||
- name: Check if Scaleway files changed
|
||||
if: steps.check-changes.outputs.any_changed == 'true'
|
||||
id: changed-scaleway
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files: |
|
||||
./prowler/**/scaleway/**
|
||||
./tests/**/scaleway/**
|
||||
./uv.lock
|
||||
|
||||
- name: Run Scaleway tests
|
||||
if: steps.changed-scaleway.outputs.any_changed == 'true'
|
||||
run: uv run pytest -n auto --cov=./prowler/providers/scaleway --cov-report=xml:scaleway_coverage.xml tests/providers/scaleway
|
||||
|
||||
- name: Upload Scaleway coverage to Codecov
|
||||
if: steps.changed-scaleway.outputs.any_changed == 'true'
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
flags: prowler-py${{ matrix.python-version }}-scaleway
|
||||
files: ./scaleway_coverage.xml
|
||||
|
||||
# StackIT Provider
|
||||
- name: Check if StackIT files changed
|
||||
if: steps.check-changes.outputs.any_changed == 'true'
|
||||
id: changed-stackit
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6
|
||||
with:
|
||||
files: |
|
||||
./prowler/**/stackit/**
|
||||
./tests/**/stackit/**
|
||||
./uv.lock
|
||||
|
||||
- name: Run StackIT tests
|
||||
if: steps.changed-stackit.outputs.any_changed == 'true'
|
||||
run: uv run pytest -n auto --cov=./prowler/providers/stackit --cov-report=xml:stackit_coverage.xml tests/providers/stackit
|
||||
|
||||
- name: Upload StackIT coverage to Codecov
|
||||
if: steps.changed-stackit.outputs.any_changed == 'true'
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
flags: prowler-py${{ matrix.python-version }}-stackit
|
||||
files: ./stackit_coverage.xml
|
||||
|
||||
# Lib
|
||||
- name: Check if Lib files changed
|
||||
if: steps.check-changes.outputs.any_changed == 'true'
|
||||
|
||||
@@ -172,7 +172,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: '24.13.0'
|
||||
node-version-file: 'ui/.nvmrc'
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
|
||||
|
||||
@@ -16,7 +16,6 @@ concurrency:
|
||||
|
||||
env:
|
||||
UI_WORKING_DIR: ./ui
|
||||
NODE_VERSION: "24.13.0"
|
||||
|
||||
permissions: {}
|
||||
|
||||
@@ -93,11 +92,11 @@ jobs:
|
||||
ui/vitest.config.ts
|
||||
ui/vitest.setup.ts
|
||||
|
||||
- name: Setup Node.js ${{ env.NODE_VERSION }}
|
||||
- name: Setup Node.js
|
||||
if: steps.check-changes.outputs.any_changed == 'true'
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version-file: 'ui/.nvmrc'
|
||||
|
||||
- name: Setup pnpm
|
||||
if: steps.check-changes.outputs.any_changed == 'true'
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "markdownlint/style/prettier",
|
||||
"first-line-h1": false,
|
||||
"no-duplicate-heading": {
|
||||
"siblings_only": true
|
||||
},
|
||||
"no-inline-html": false,
|
||||
"line-length": false,
|
||||
"no-bare-urls": false
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
node_modules/
|
||||
ui/node_modules/
|
||||
.git/
|
||||
.venv/
|
||||
**/.venv/
|
||||
dist/
|
||||
build/
|
||||
htmlcov/
|
||||
.next/
|
||||
ui/.next/
|
||||
ui/out/
|
||||
contrib/
|
||||
|
||||
# Auto-generated content (keepachangelog format legitimately repeats section headings).
|
||||
# Revisit with the team — see beads task on markdownlint rule triage.
|
||||
**/CHANGELOG.md
|
||||
@@ -133,6 +133,13 @@ repos:
|
||||
pass_filenames: false
|
||||
priority: 50
|
||||
|
||||
## MARKDOWN
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.45.0
|
||||
hooks:
|
||||
- id: markdownlint
|
||||
priority: 30
|
||||
|
||||
## CONTAINERS
|
||||
- repo: https://github.com/hadolint/hadolint
|
||||
rev: v2.14.0
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
Use these skills for detailed patterns on-demand:
|
||||
|
||||
### Generic Skills (Any Project)
|
||||
|
||||
| Skill | Description | URL |
|
||||
|-------|-------------|-----|
|
||||
| `typescript` | Const types, flat interfaces, utility types | [SKILL.md](skills/typescript/SKILL.md) |
|
||||
@@ -28,6 +29,7 @@ Use these skills for detailed patterns on-demand:
|
||||
| `tdd` | Test-Driven Development workflow | [SKILL.md](skills/tdd/SKILL.md) |
|
||||
|
||||
### Prowler-Specific Skills
|
||||
|
||||
| Skill | Description | URL |
|
||||
|-------|-------------|-----|
|
||||
| `prowler` | Project overview, component navigation | [SKILL.md](skills/prowler/SKILL.md) |
|
||||
|
||||
+4
-3
@@ -1,4 +1,4 @@
|
||||
# Do you want to learn on how to...
|
||||
# Do you want to learn on how to
|
||||
|
||||
- [Contribute with your code or fixes to Prowler](https://docs.prowler.com/developer-guide/introduction)
|
||||
- [Create a new provider](https://docs.prowler.com/developer-guide/provider)
|
||||
@@ -32,5 +32,6 @@ Provider-specific developer notes:
|
||||
|
||||
Want some swag as appreciation for your contribution?
|
||||
|
||||
# Prowler Developer Guide
|
||||
https://goto.prowler.com/devguide
|
||||
## Prowler Developer Guide
|
||||
|
||||
<https://goto.prowler.com/devguide>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<p align="center">
|
||||
<img align="center" src="https://github.com/prowler-cloud/prowler/blob/master/docs/img/prowler-logo-black.png#gh-light-mode-only" width="50%" height="50%">
|
||||
<img align="center" src="https://github.com/prowler-cloud/prowler/blob/master/docs/img/prowler-logo-white.png#gh-dark-mode-only" width="50%" height="50%">
|
||||
<img align="center" alt="Prowler logo" src="https://github.com/prowler-cloud/prowler/blob/master/docs/img/prowler-logo-black.png#gh-light-mode-only" width="50%" height="50%">
|
||||
<img align="center" alt="Prowler logo" src="https://github.com/prowler-cloud/prowler/blob/master/docs/img/prowler-logo-white.png#gh-dark-mode-only" width="50%" height="50%">
|
||||
</p>
|
||||
<p align="center">
|
||||
<b><i>Prowler</b> is the Open Cloud Security Platform trusted by thousands to automate security and compliance in any cloud environment. With hundreds of ready-to-use checks and compliance frameworks, Prowler delivers real-time, customizable monitoring and seamless integrations, making cloud security simple, scalable, and cost-effective for organizations of any size.
|
||||
@@ -22,8 +22,8 @@
|
||||
<a href="https://pypistats.org/packages/prowler"><img alt="PyPI Downloads" src="https://img.shields.io/pypi/dw/prowler.svg?label=downloads"></a>
|
||||
<a href="https://hub.docker.com/r/toniblyx/prowler"><img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/toniblyx/prowler"></a>
|
||||
<a href="https://gallery.ecr.aws/prowler-cloud/prowler"><img width="120" height=19" alt="AWS ECR Gallery" src="https://user-images.githubusercontent.com/3985464/151531396-b6535a68-c907-44eb-95a1-a09508178616.png"></a>
|
||||
<a href="https://codecov.io/gh/prowler-cloud/prowler"><img src="https://codecov.io/gh/prowler-cloud/prowler/graph/badge.svg?token=OflBGsdpDl"/></a>
|
||||
<a href="https://insights.linuxfoundation.org/project/prowler-cloud-prowler"><img src="https://insights.linuxfoundation.org/api/badge/health-score?project=prowler-cloud-prowler"/></a>
|
||||
<a href="https://codecov.io/gh/prowler-cloud/prowler"><img alt="Codecov coverage" src="https://codecov.io/gh/prowler-cloud/prowler/graph/badge.svg?token=OflBGsdpDl"/></a>
|
||||
<a href="https://insights.linuxfoundation.org/project/prowler-cloud-prowler"><img alt="Linux Foundation insights health score" src="https://insights.linuxfoundation.org/api/badge/health-score?project=prowler-cloud-prowler"/></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/prowler-cloud/prowler/releases"><img alt="Version" src="https://img.shields.io/github/v/release/prowler-cloud/prowler"></a>
|
||||
@@ -36,7 +36,7 @@
|
||||
</p>
|
||||
<hr>
|
||||
<p align="center">
|
||||
<img align="center" src="/docs/img/prowler-cloud.gif" width="100%" height="100%">
|
||||
<img align="center" alt="Prowler Cloud demo" src="/docs/img/prowler-cloud.gif" width="100%" height="100%">
|
||||
</p>
|
||||
|
||||
# Description
|
||||
@@ -122,6 +122,7 @@ Every AWS provider scan will enqueue an Attack Paths ingestion job automatically
|
||||
| Vercel | 26 | 6 | 0 | 8 | Official | UI, API, CLI |
|
||||
| Okta | 1 | 1 | 0 | 1 | Official | CLI |
|
||||
| Scaleway [Contact us](https://prowler.com/contact) | 1 | 1 | 0 | 1 | Unofficial | CLI |
|
||||
| StackIT [Contact us](https://prowler.com/contact) | 4 | 1 | 0 | 1 | Unofficial | CLI |
|
||||
| NHN | 6 | 2 | 1 | 0 | Unofficial | CLI |
|
||||
|
||||
> [!Note]
|
||||
@@ -146,11 +147,11 @@ Prowler App offers flexible installation methods tailored to various environment
|
||||
|
||||
### Docker Compose
|
||||
|
||||
**Requirements**
|
||||
#### Requirements
|
||||
|
||||
* `Docker Compose` installed: https://docs.docker.com/compose/install/.
|
||||
- `Docker Compose` installed: https://docs.docker.com/compose/install/.
|
||||
|
||||
**Commands**
|
||||
#### Commands
|
||||
|
||||
``` console
|
||||
VERSION=$(curl -s https://api.github.com/repos/prowler-cloud/prowler/releases/latest | jq -r .tag_name)
|
||||
@@ -175,14 +176,14 @@ You can find more information in the [Troubleshooting](./docs/troubleshooting.md
|
||||
|
||||
### From GitHub
|
||||
|
||||
**Requirements**
|
||||
#### Requirements
|
||||
|
||||
* `git` installed.
|
||||
* `uv` installed: [uv installation](https://docs.astral.sh/uv/getting-started/installation/).
|
||||
* `pnpm` installed: [pnpm installation](https://pnpm.io/installation).
|
||||
* `Docker Compose` installed: https://docs.docker.com/compose/install/.
|
||||
- `git` installed.
|
||||
- `uv` installed: [uv installation](https://docs.astral.sh/uv/getting-started/installation/).
|
||||
- `pnpm` installed: [pnpm installation](https://pnpm.io/installation).
|
||||
- `Docker Compose` installed: https://docs.docker.com/compose/install/.
|
||||
|
||||
**Commands to run the API**
|
||||
#### Commands to run the API
|
||||
|
||||
``` console
|
||||
git clone https://github.com/prowler-cloud/prowler
|
||||
@@ -199,7 +200,7 @@ gunicorn -c config/guniconf.py config.wsgi:application
|
||||
|
||||
> After completing the setup, access the API documentation at http://localhost:8080/api/v1/docs.
|
||||
|
||||
**Commands to run the API Worker**
|
||||
#### Commands to run the API Worker
|
||||
|
||||
``` console
|
||||
git clone https://github.com/prowler-cloud/prowler
|
||||
@@ -212,7 +213,7 @@ cd src/backend
|
||||
python -m celery -A config.celery worker -l info -E
|
||||
```
|
||||
|
||||
**Commands to run the API Scheduler**
|
||||
#### Commands to run the API Scheduler
|
||||
|
||||
``` console
|
||||
git clone https://github.com/prowler-cloud/prowler
|
||||
@@ -225,7 +226,7 @@ cd src/backend
|
||||
python -m celery -A config.celery beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
|
||||
```
|
||||
|
||||
**Commands to run the UI**
|
||||
#### Commands to run the UI
|
||||
|
||||
``` console
|
||||
git clone https://github.com/prowler-cloud/prowler
|
||||
@@ -237,7 +238,7 @@ pnpm start
|
||||
|
||||
> Once configured, access the Prowler App at http://localhost:3000. Sign up using your email and password to get started.
|
||||
|
||||
**Pre-commit Hooks Setup**
|
||||
#### Pre-commit Hooks Setup
|
||||
|
||||
Some pre-commit hooks require tools installed on your system:
|
||||
|
||||
@@ -257,14 +258,14 @@ prowler -v
|
||||
|
||||
### Containers
|
||||
|
||||
**Available Versions of Prowler CLI**
|
||||
#### Available Versions of Prowler CLI
|
||||
|
||||
The following versions of Prowler CLI are available, depending on your requirements:
|
||||
|
||||
- `latest`: Synchronizes with the `master` branch. Note that this version is not stable.
|
||||
- `v4-latest`: Synchronizes with the `v4` branch. Note that this version is not stable.
|
||||
- `v3-latest`: Synchronizes with the `v3` branch. Note that this version is not stable.
|
||||
- `<x.y.z>` (release): Stable releases corresponding to specific versions. You can find the complete list of releases [here](https://github.com/prowler-cloud/prowler/releases).
|
||||
- `<x.y.z>` (release): Stable releases corresponding to specific versions. See the [complete list of Prowler releases](https://github.com/prowler-cloud/prowler/releases).
|
||||
- `stable`: Always points to the latest release.
|
||||
- `v4-stable`: Always points to the latest release for v4.
|
||||
- `v3-stable`: Always points to the latest release for v3.
|
||||
@@ -293,7 +294,7 @@ python prowler-cli.py -v
|
||||
|
||||
# 🛡️ GitHub Action
|
||||
|
||||
The official **Prowler GitHub Action** runs Prowler scans in your GitHub workflows using the official [`prowlercloud/prowler`](https://hub.docker.com/r/prowlercloud/prowler) Docker image. Scans run on any [supported provider](https://docs.prowler.com/user-guide/providers/), with optional [`--push-to-cloud`](https://docs.prowler.com/user-guide/tutorials/prowler-app-import-findings) to send findings to Prowler Cloud and optional SARIF upload so findings show up in the repo's **Security → Code scanning** tab and as inline PR annotations.
|
||||
The official **Prowler GitHub Action** runs Prowler scans in your GitHub workflows using the official [`prowlercloud/prowler`](https://hub.docker.com/r/prowlercloud/prowler) Docker image. Scans run on any [supported provider](https://docs.prowler.com/user-guide/providers/), with optional [`--push-to-cloud`](https://docs.prowler.com/user-guide/tutorials/prowler-import-findings) to send findings to Prowler Cloud and optional SARIF upload so findings show up in the repo's **Security → Code scanning** tab and as inline PR annotations.
|
||||
|
||||
```yaml
|
||||
name: Prowler IaC Scan
|
||||
@@ -338,7 +339,7 @@ Full configuration, per-provider authentication, and SARIF examples: [Prowler Gi
|
||||
|
||||
## Prowler CLI
|
||||
|
||||
**Running Prowler**
|
||||
### Running Prowler
|
||||
|
||||
Prowler can be executed across various environments, offering flexibility to meet your needs. It can be run from:
|
||||
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@ inputs:
|
||||
required: false
|
||||
default: json-ocsf
|
||||
push-to-cloud:
|
||||
description: Push scan findings to Prowler Cloud. Requires the PROWLER_CLOUD_API_KEY environment variable. See https://docs.prowler.com/user-guide/tutorials/prowler-app-import-findings#using-the-cli
|
||||
description: Push scan findings to Prowler Cloud. Requires the PROWLER_CLOUD_API_KEY environment variable. See https://docs.prowler.com/user-guide/tutorials/prowler-import-findings#using-the-cli
|
||||
required: false
|
||||
default: "false"
|
||||
flags:
|
||||
@@ -299,7 +299,7 @@ runs:
|
||||
echo ""
|
||||
echo "**Get started in 3 steps:**"
|
||||
echo "1. Create an account at [cloud.prowler.com](https://cloud.prowler.com)"
|
||||
echo "2. Generate a Prowler Cloud API key ([docs](https://docs.prowler.com/user-guide/tutorials/prowler-app-import-findings#using-the-cli))"
|
||||
echo "2. Generate a Prowler Cloud API key ([docs](https://docs.prowler.com/user-guide/tutorials/prowler-import-findings#using-the-cli))"
|
||||
echo "3. Add \`PROWLER_CLOUD_API_KEY\` to your GitHub secrets and set \`push-to-cloud: true\` on this action"
|
||||
echo ""
|
||||
echo "See [prowler.com/pricing](https://prowler.com/pricing) for plan details."
|
||||
|
||||
+4
-4
@@ -10,7 +10,7 @@
|
||||
> - [`jsonapi`](../skills/jsonapi/SKILL.md) - Strict JSON:API v1.1 spec compliance
|
||||
> - [`pytest`](../skills/pytest/SKILL.md) - Generic pytest patterns
|
||||
|
||||
### Auto-invoke Skills
|
||||
## Auto-invoke Skills
|
||||
|
||||
When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
|
||||
@@ -81,7 +81,7 @@ When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
## DECISION TREES
|
||||
|
||||
### Serializer Selection
|
||||
```
|
||||
```text
|
||||
Read → <Model>Serializer
|
||||
Create → <Model>CreateSerializer
|
||||
Update → <Model>UpdateSerializer
|
||||
@@ -89,7 +89,7 @@ Nested read → <Model>IncludeSerializer
|
||||
```
|
||||
|
||||
### Task vs View
|
||||
```
|
||||
```text
|
||||
< 100ms → View
|
||||
> 100ms or external API → Celery task
|
||||
Needs retry → Celery task
|
||||
@@ -105,7 +105,7 @@ Django 5.1.x | DRF 3.15.x | djangorestframework-jsonapi 7.x | Celery 5.4.x | Pos
|
||||
|
||||
## PROJECT STRUCTURE
|
||||
|
||||
```
|
||||
```text
|
||||
api/src/backend/
|
||||
├── api/ # Main Django app
|
||||
│ ├── v1/ # API version 1 (views, serializers, urls)
|
||||
|
||||
+2
-1
@@ -2,11 +2,12 @@
|
||||
|
||||
All notable changes to the **Prowler API** are documented in this file.
|
||||
|
||||
## [1.30.0] (Prowler UNRELEASED)
|
||||
## [1.30.0] (Prowler v5.29.0)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
- Scan finding ingestion: bulk-resolve `Resource`/`ResourceTag` rows, replace per-mapping `SELECT FOR UPDATE` with deferred `ResourceTagMapping.bulk_create(ignore_conflicts=True)`, wrap each micro-batch in a single `rls_transaction`, and raise `SCAN_DB_BATCH_SIZE` to 1000 [(#11249)](https://github.com/prowler-cloud/prowler/pull/11249)
|
||||
- Faster `GET /api/v1/finding-groups/latest` aggregation on tenants where one recent scan holds most findings [(#11380)](https://github.com/prowler-cloud/prowler/pull/11380)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+29
-29
@@ -2,7 +2,7 @@
|
||||
|
||||
This repository contains the JSON API and Task Runner components for Prowler, which facilitate a complete backend that interacts with the Prowler SDK and is used by the Prowler UI.
|
||||
|
||||
# Components
|
||||
## Components
|
||||
The Prowler API is composed of the following components:
|
||||
|
||||
- The JSON API, which is an API built with Django Rest Framework.
|
||||
@@ -10,13 +10,13 @@ The Prowler API is composed of the following components:
|
||||
- The PostgreSQL database, which is used to store the data.
|
||||
- The Valkey database, which is an in-memory database which is used as a message broker for the Celery workers.
|
||||
|
||||
## Note about Valkey
|
||||
### Note about Valkey
|
||||
|
||||
[Valkey](https://valkey.io/) is an open source (BSD) high performance key/value datastore.
|
||||
|
||||
Valkey exposes a Redis 7.2 compliant API. Any service that exposes the Redis API can be used with Prowler API.
|
||||
|
||||
# Modify environment variables
|
||||
## Modify environment variables
|
||||
|
||||
Under the root path of the project, you can find a file called `.env`. This file shows all the environment variables that the project uses. You should review it and set the values for the variables you want to change.
|
||||
|
||||
@@ -24,7 +24,7 @@ If you don’t set `DJANGO_TOKEN_SIGNING_KEY` or `DJANGO_TOKEN_VERIFYING_KEY`, t
|
||||
|
||||
**Important note**: Every Prowler version (or repository branches and tags) could have different variables set in its `.env` file. Please use the `.env` file that corresponds with each version.
|
||||
|
||||
## Local deployment
|
||||
### Local deployment
|
||||
Keep in mind if you export the `.env` file to use it with local deployment that you will have to do it within the context of the virtual environment, not before. Otherwise, variables will not be loaded properly.
|
||||
|
||||
To do this, you can run:
|
||||
@@ -34,12 +34,12 @@ set -a
|
||||
source .env
|
||||
```
|
||||
|
||||
# 🚀 Production deployment
|
||||
## Docker deployment
|
||||
## 🚀 Production deployment
|
||||
### Docker deployment
|
||||
|
||||
This method requires `docker` and `docker compose`.
|
||||
|
||||
### Clone the repository
|
||||
#### Clone the repository
|
||||
|
||||
```console
|
||||
# HTTPS
|
||||
@@ -50,13 +50,13 @@ git clone git@github.com:prowler-cloud/api.git
|
||||
|
||||
```
|
||||
|
||||
### Build the base image
|
||||
#### Build the base image
|
||||
|
||||
```console
|
||||
docker compose --profile prod build
|
||||
```
|
||||
|
||||
### Run the production service
|
||||
#### Run the production service
|
||||
|
||||
This command will start the Django production server and the Celery worker and also the Valkey and PostgreSQL databases.
|
||||
|
||||
@@ -68,7 +68,7 @@ You can access the server in `http://localhost:8080`.
|
||||
|
||||
> **NOTE:** notice how the port is different. When developing using docker, the port will be `8080` to prevent conflicts.
|
||||
|
||||
### View the Production Server Logs
|
||||
#### View the Production Server Logs
|
||||
|
||||
To view the logs for any component (e.g., Django, Celery worker), you can use the following command with a wildcard. This command will follow logs for any container that matches the specified pattern:
|
||||
|
||||
@@ -133,13 +133,13 @@ gunicorn -c config/guniconf.py config.wsgi:application
|
||||
|
||||
> By default, the Gunicorn server will try to use as many workers as your machine can handle. You can manually change that in the `src/backend/config/guniconf.py` file.
|
||||
|
||||
# 🧪 Development guide
|
||||
## 🧪 Development guide
|
||||
|
||||
## Local deployment
|
||||
### Local deployment
|
||||
|
||||
To use this method, you'll need to set up a Python virtual environment (version ">=3.11,<3.13") and keep dependencies updated. Additionally, ensure that `uv` and `docker compose` are installed.
|
||||
|
||||
### Clone the repository
|
||||
#### Clone the repository
|
||||
|
||||
```console
|
||||
# HTTPS
|
||||
@@ -150,7 +150,7 @@ git clone git@github.com:prowler-cloud/api.git
|
||||
|
||||
```
|
||||
|
||||
### Start the PostgreSQL Database and Valkey
|
||||
#### Start the PostgreSQL Database and Valkey
|
||||
|
||||
The PostgreSQL database (version 16.3) and Valkey (version 7) are required for the development environment. To make development easier, we have provided a `docker-compose` file that will start these components for you.
|
||||
|
||||
@@ -161,7 +161,7 @@ The PostgreSQL database (version 16.3) and Valkey (version 7) are required for t
|
||||
docker compose up postgres valkey -d
|
||||
```
|
||||
|
||||
### Install the Python dependencies
|
||||
#### Install the Python dependencies
|
||||
|
||||
> You must have uv installed
|
||||
|
||||
@@ -169,7 +169,7 @@ docker compose up postgres valkey -d
|
||||
uv sync
|
||||
```
|
||||
|
||||
### Apply migrations
|
||||
#### Apply migrations
|
||||
|
||||
For migrations, you need to force the `admin` database router. Assuming you have the correct environment variables and Python virtual environment, run:
|
||||
|
||||
@@ -178,7 +178,7 @@ cd src/backend
|
||||
python manage.py migrate --database admin
|
||||
```
|
||||
|
||||
### Run the Django development server
|
||||
#### Run the Django development server
|
||||
|
||||
```console
|
||||
cd src/backend
|
||||
@@ -188,7 +188,7 @@ python manage.py runserver
|
||||
You can access the server in `http://localhost:8000`.
|
||||
All changes in the code will be automatically reloaded in the server.
|
||||
|
||||
### Run the Celery worker
|
||||
#### Run the Celery worker
|
||||
|
||||
```console
|
||||
python -m celery -A config.celery worker -l info -E
|
||||
@@ -196,11 +196,11 @@ python -m celery -A config.celery worker -l info -E
|
||||
|
||||
The Celery worker does not detect and reload changes in the code, so you need to restart it manually when you make changes.
|
||||
|
||||
## Docker deployment
|
||||
### Docker deployment
|
||||
|
||||
This method requires `docker` and `docker compose`.
|
||||
|
||||
### Clone the repository
|
||||
#### Clone the repository
|
||||
|
||||
```console
|
||||
# HTTPS
|
||||
@@ -211,13 +211,13 @@ git clone git@github.com:prowler-cloud/api.git
|
||||
|
||||
```
|
||||
|
||||
### Build the base image
|
||||
#### Build the base image
|
||||
|
||||
```console
|
||||
docker compose --profile dev build
|
||||
```
|
||||
|
||||
### Run the development service
|
||||
#### Run the development service
|
||||
|
||||
This command will start the Django development server and the Celery worker and also the Valkey and PostgreSQL databases.
|
||||
|
||||
@@ -230,7 +230,7 @@ All changes in the code will be automatically reloaded in the server.
|
||||
|
||||
> **NOTE:** notice how the port is different. When developing using docker, the port will be `8080` to prevent conflicts.
|
||||
|
||||
### View the development server logs
|
||||
#### View the development server logs
|
||||
|
||||
To view the logs for any component (e.g., Django, Celery worker), you can use the following command with a wildcard. This command will follow logs for any container that matches the specified pattern:
|
||||
|
||||
@@ -238,7 +238,7 @@ To view the logs for any component (e.g., Django, Celery worker), you can use th
|
||||
docker logs -f $(docker ps --format "{{.Names}}" | grep 'api-')
|
||||
```
|
||||
|
||||
## Applying migrations
|
||||
### Applying migrations
|
||||
|
||||
For migrations, you need to force the `admin` database router. Assuming you have the correct environment variables and Python virtual environment, run:
|
||||
|
||||
@@ -247,7 +247,7 @@ cd src/backend
|
||||
uv run python manage.py migrate --database admin
|
||||
```
|
||||
|
||||
## Apply fixtures
|
||||
### Apply fixtures
|
||||
|
||||
Fixtures are used to populate the database with initial development data.
|
||||
|
||||
@@ -258,7 +258,7 @@ uv run python manage.py loaddata api/fixtures/0_dev_users.json --database admin
|
||||
|
||||
> The default credentials are `dev@prowler.com:Thisisapassword123@` or `dev2@prowler.com:Thisisapassword123@`
|
||||
|
||||
## Run tests
|
||||
### Run tests
|
||||
|
||||
Note that the tests will fail if you use the same `.env` file as the development environment.
|
||||
|
||||
@@ -269,7 +269,7 @@ cd src/backend
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
# Custom commands
|
||||
## Custom commands
|
||||
|
||||
Django provides a way to create custom commands that can be run from the command line.
|
||||
|
||||
@@ -281,7 +281,7 @@ To run a custom command, you need to be in the `prowler/api/src/backend` directo
|
||||
uv run python manage.py <command_name>
|
||||
```
|
||||
|
||||
## Generate dummy data
|
||||
### Generate dummy data
|
||||
|
||||
```console
|
||||
python manage.py findings --tenant
|
||||
@@ -298,7 +298,7 @@ This command creates, for a given tenant, a provider, scan and a set of findings
|
||||
>
|
||||
> The last step is required to access the findings details, since the UI needs that to print all the information.
|
||||
|
||||
### Example
|
||||
#### Example
|
||||
|
||||
```console
|
||||
~/backend $ uv run python manage.py findings --tenant
|
||||
|
||||
@@ -7484,14 +7484,17 @@ class FindingGroupViewSet(BaseRLSViewSet):
|
||||
|
||||
def _get_latest_findings_per_provider(self, filtered_queryset):
|
||||
"""Keep only findings from each provider's most recent completed scan."""
|
||||
latest_scan_ids = (
|
||||
# Materialize to a literal IN list. Left as a subquery, Postgres can't
|
||||
# estimate the match count and picks a serial nested loop on
|
||||
# resource_finding_mappings when one scan dominates findings
|
||||
latest_scan_ids = list(
|
||||
Scan.objects.filter(
|
||||
tenant_id=self.request.tenant_id,
|
||||
state=StateChoices.COMPLETED,
|
||||
)
|
||||
.order_by("provider_id", "-completed_at", "-inserted_at")
|
||||
.distinct("provider_id")
|
||||
.values("id")
|
||||
.values_list("id", flat=True)
|
||||
)
|
||||
return filtered_queryset.filter(scan_id__in=latest_scan_ids)
|
||||
|
||||
|
||||
+2
-2
@@ -134,7 +134,7 @@ Example 1 is vague and even potentially ambiguous. Verbs state your purpose and
|
||||
|
||||
Explicit use of second-person pronouns (you) and possessives (your) should be minimized whenever possible. Those constructions are best reserved for cases when instructions are directly given in an imperative form:
|
||||
|
||||
**Example of Improvement Through Avoiding Second Person Pronouns**
|
||||
### Example of Improvement Through Avoiding Second Person Pronouns
|
||||
|
||||
**Original:**
|
||||
Prowler App can be installed in different ways, depending on your environment:
|
||||
@@ -236,7 +236,7 @@ The use of bullet points is highly recommended when:
|
||||
* Information can be logically divided into multiple categories, each sharing characteristics, features, or other relevant classifications.
|
||||
* Items are significant enough as standalone concepts to deserve their own bullet point.
|
||||
|
||||
**Example of Improvement Through Bullet Points**
|
||||
#### Example of Improvement Through Bullet Points
|
||||
|
||||
**Original:**
|
||||
It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, FedRAMS, 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.
|
||||
|
||||
@@ -0,0 +1,730 @@
|
||||
---
|
||||
title: 'StackIT Provider'
|
||||
---
|
||||
|
||||
This page details the [StackIT Cloud](https://www.stackit.de/) provider implementation in Prowler.
|
||||
|
||||
By default, Prowler audits a single StackIT project per scan. To configure it, provide the project ID and either a service account key file path or inline service account key JSON.
|
||||
|
||||
## StackIT Provider Classes Architecture
|
||||
|
||||
The StackIT provider implementation follows the general [Provider structure](/developer-guide/provider). This section focuses on the StackIT-specific implementation, highlighting how the generic provider concepts are realized for StackIT in Prowler. For a full overview of the provider pattern, base classes, and extension guidelines, see [Provider documentation](/developer-guide/provider).
|
||||
|
||||
### `StackitProvider` (Main Class)
|
||||
|
||||
- **Location:** [`prowler/providers/stackit/stackit_provider.py`](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/stackit/stackit_provider.py)
|
||||
- **Base Class:** Inherits from `Provider` (see [base class details](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/common/provider.py)).
|
||||
- **Purpose:** Central orchestrator for StackIT-specific logic, API authentication, credential validation, and configuration.
|
||||
- **Key StackIT Responsibilities:**
|
||||
- Initializes StackIT SDK authentication via a service account key file or inline service account key JSON. The SDK mints and refreshes access tokens internally.
|
||||
- Validates the service account credentials and project ID (UUID format validation).
|
||||
- Loads and manages configuration, mutelist, and fixer settings.
|
||||
- Provides properties and methods for downstream StackIT service classes to access credentials, identity, and configuration data.
|
||||
|
||||
### Data Models
|
||||
|
||||
- **Location:** [`prowler/providers/stackit/models.py`](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/stackit/models.py)
|
||||
- **Purpose:** Define structured data for StackIT identity and output configuration.
|
||||
- **Key StackIT Models:**
|
||||
- `StackITIdentityInfo`: Holds StackIT identity metadata, including project ID and project name (fetched automatically from Resource Manager API).
|
||||
- `StackITOutputOptions`: Customizes default output filenames so StackIT reports include the audited project ID.
|
||||
- IaaS resource models such as `SecurityGroup` and `SecurityGroupRule` are defined in the IaaS service module.
|
||||
|
||||
### StackIT Services
|
||||
|
||||
- **Location:** [`prowler/providers/stackit/services/`](https://github.com/prowler-cloud/prowler/tree/master/prowler/providers/stackit/services)
|
||||
- **Purpose:** Implement StackIT service clients and resource collection logic following the generic [service pattern](/developer-guide/services#service-base-class).
|
||||
- **Current Implementation:** The `IaaSService` collects security groups, rules, and network interface usage across supported StackIT regions.
|
||||
|
||||
### Exception Handling
|
||||
|
||||
- **Location:** [`prowler/providers/stackit/exceptions/exceptions.py`](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/stackit/exceptions/exceptions.py)
|
||||
- **Purpose:** Custom exception classes for StackIT-specific error handling, such as credential validation, API connection, and configuration errors.
|
||||
- **Key Exception Classes:**
|
||||
- `StackITBaseException`: Base exception for all StackIT provider errors.
|
||||
- `StackITCredentialsError`: Raised when credentials are invalid or missing.
|
||||
- `StackITInvalidProjectIdError`: Raised when project ID is invalid or not in UUID format.
|
||||
- `StackITAPIError`: Raised when StackIT API calls fail.
|
||||
|
||||
## Authentication
|
||||
|
||||
### Service Account Creation and Key Generation
|
||||
|
||||
StackIT uses service account keys for API authentication. Service account keys are RSA key-pair based and provide secure, short-lived access tokens.
|
||||
|
||||
### Creating a Service Account Key
|
||||
|
||||
#### Method 1: Via StackIT Portal
|
||||
|
||||
1. **Navigate to Service Accounts**
|
||||
- Go to the [StackIT Portal](https://portal.stackit.cloud/)
|
||||
- Select your project
|
||||
- Click on **Service Accounts** in the left sidebar
|
||||
|
||||
2. **Create or Select Service Account**
|
||||
- If you don't have a service account, click **Create Service Account**
|
||||
- Provide a name and description
|
||||
- Assign necessary permissions:
|
||||
- For IaaS security checks: `iaas.viewer` or `project.owner`
|
||||
- For comprehensive audits: `project.owner`
|
||||
|
||||
3. **Generate Service Account Key**
|
||||
- Select your service account
|
||||
- Navigate to **Service Account Keys**
|
||||
- Click **Create key**
|
||||
- Choose one of the following options:
|
||||
- **STACKIT-generated key pair** (Recommended): Let STACKIT automatically generate an RSA key-pair
|
||||
- **User-provided key pair**: Upload your own RSA 2048 public key
|
||||
|
||||
4. **Download and Save the Key**
|
||||
- Download the generated service account key file (JSON format)
|
||||
- **Important**: Save the key securely - it contains your private key and will only be available once
|
||||
- Store the key file in a secure location (e.g., `~/.stackit/sa_key.json`)
|
||||
|
||||
#### Method 2: Via StackIT CLI
|
||||
|
||||
```bash
|
||||
# Install STACKIT CLI (if not already installed)
|
||||
# Follow instructions at: https://github.com/stackitcloud/stackit-cli
|
||||
|
||||
# Create service account key (STACKIT-generated)
|
||||
stackit service-account key create --email my-service-account@example.com
|
||||
|
||||
# Or create with your own RSA 2048 public key
|
||||
# First, generate your RSA key pair:
|
||||
openssl genrsa -out private-key.pem 2048
|
||||
openssl rsa -in private-key.pem -pubout -out public-key.pem
|
||||
|
||||
# Then create the key with your public key:
|
||||
stackit service-account key create \
|
||||
--email my-service-account@example.com \
|
||||
--public-key "$(cat public-key.pem)"
|
||||
```
|
||||
|
||||
### Finding Your Project ID
|
||||
|
||||
Your StackIT project ID is a UUID that can be found:
|
||||
|
||||
1. In the StackIT Portal URL when viewing your project: `https://portal.stackit.cloud/projects/{PROJECT_ID}/...`
|
||||
2. In the project settings page
|
||||
3. Using the StackIT CLI: `stackit project list`
|
||||
|
||||
### Passing the Service Account Key to Prowler
|
||||
|
||||
Prowler accepts the service account credentials in two equivalent forms; both go through the same StackIT SDK flow and refresh access tokens internally.
|
||||
|
||||
#### Option 1: Key File Path (key persisted on disk)
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY_PATH="$HOME/.stackit/sa-key.json"
|
||||
export STACKIT_PROJECT_ID="12345678-1234-1234-1234-123456789abc"
|
||||
|
||||
prowler stackit
|
||||
```
|
||||
|
||||
Or as CLI flags:
|
||||
|
||||
```bash
|
||||
prowler stackit \
|
||||
--stackit-service-account-key-path ~/.stackit/sa-key.json \
|
||||
--stackit-project-id 12345678-1234-1234-1234-123456789abc
|
||||
```
|
||||
|
||||
#### Option 2: Inline Key Content (CI/CD, secret managers)
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY="$(vault kv get -field=key stackit/sa)"
|
||||
export STACKIT_PROJECT_ID="12345678-1234-1234-1234-123456789abc"
|
||||
|
||||
prowler stackit
|
||||
```
|
||||
|
||||
Prefer the environment variable over the matching `--stackit-service-account-key` CLI flag; passing the secret on the command line leaks it through process listings and shell history.
|
||||
|
||||
### Credential Lookup Order
|
||||
|
||||
Prowler resolves credentials in this order:
|
||||
|
||||
1. **Command-line arguments**:
|
||||
- `--stackit-service-account-key`
|
||||
- `--stackit-service-account-key-path`
|
||||
- `--stackit-project-id`
|
||||
2. **Environment variables**:
|
||||
- `STACKIT_SERVICE_ACCOUNT_KEY`
|
||||
- `STACKIT_SERVICE_ACCOUNT_KEY_PATH`
|
||||
- `STACKIT_PROJECT_ID`
|
||||
|
||||
When both the inline key and the key file path are set, the inline content takes precedence.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Command-Line Arguments
|
||||
|
||||
StackIT-specific command-line arguments:
|
||||
|
||||
| Argument | Description | Required | Default |
|
||||
|----------|-------------|----------|---------|
|
||||
| `--stackit-service-account-key-path` | Path to a StackIT service account key JSON file | Yes* | `$STACKIT_SERVICE_ACCOUNT_KEY_PATH` |
|
||||
| `--stackit-service-account-key` | Inline JSON content of a StackIT service account key (preferred env var: `STACKIT_SERVICE_ACCOUNT_KEY`) | Yes* | `$STACKIT_SERVICE_ACCOUNT_KEY` |
|
||||
| `--stackit-project-id` | StackIT project ID (UUID format) | Yes* | `$STACKIT_PROJECT_ID` |
|
||||
| `--stackit-region` | StackIT region(s) to scan | No | All available regions |
|
||||
|
||||
\* Required unless provided via environment variables.
|
||||
|
||||
### Input Validation
|
||||
|
||||
The StackIT provider performs comprehensive input validation:
|
||||
|
||||
- **Service Account Credentials**:
|
||||
- At least one of `service_account_key_path` (file path) or `service_account_key` (inline JSON) must be supplied; both empty raises `StackITNonExistentTokenError`
|
||||
- When both are provided the inline content takes precedence
|
||||
- The key file path is logged as-is; the inline content is redacted in the credentials box
|
||||
|
||||
- **Project ID**:
|
||||
- Must not be empty
|
||||
- Must be a valid UUID format (e.g., `12345678-1234-1234-1234-123456789abc`)
|
||||
- Validated using Python's UUID constructor
|
||||
|
||||
Invalid credentials will result in clear error messages before any API calls are made.
|
||||
|
||||
## Available Services
|
||||
|
||||
### IaaS (Infrastructure as a Service)
|
||||
|
||||
- **Service Class:** `IaaSService`
|
||||
- **Location:** [`prowler/providers/stackit/services/iaas/iaas_service.py`](https://github.com/prowler-cloud/prowler/blob/master/prowler/providers/stackit/services/iaas/iaas_service.py)
|
||||
- **SDK:** Uses the [stackit-iaas](https://pypi.org/project/stackit-iaas/) Python SDK
|
||||
- **Purpose:** Manages IaaS resources including security groups, servers, and network interfaces.
|
||||
|
||||
**Supported Resources:**
|
||||
- Security Groups and Rules
|
||||
- Servers (Virtual Machines)
|
||||
- Network Interfaces (NICs)
|
||||
|
||||
**Key Features:**
|
||||
- Automatic discovery of all security groups in the project
|
||||
- Security rule parsing with support for unrestricted access detection
|
||||
- Network interface analysis to determine whether security groups are in use
|
||||
- By default, reports only security groups attached to at least one NIC; `--scan-unused-services` includes unused security groups too
|
||||
|
||||
## Available Checks
|
||||
|
||||
The StackIT provider currently implements 4 security checks focused on network security:
|
||||
|
||||
### 1. iaas_security_group_ssh_unrestricted
|
||||
|
||||
- **Severity:** High
|
||||
- **Description:** Detects security groups that allow unrestricted SSH access (port 22) from the internet.
|
||||
- **Risk:** Unrestricted SSH access increases the attack surface and risk of brute-force attacks.
|
||||
- **Detection Logic:**
|
||||
- Checks for ingress rules allowing TCP port 22
|
||||
- Flags rules with `ip_range=None` or `ip_range="0.0.0.0/0"` or `ip_range="::/0"`
|
||||
- Reports security groups attached to NICs by default, or all security groups when `--scan-unused-services` is enabled
|
||||
|
||||
### 2. iaas_security_group_rdp_unrestricted
|
||||
|
||||
- **Severity:** High
|
||||
- **Description:** Detects security groups that allow unrestricted RDP access (port 3389) from the internet.
|
||||
- **Risk:** Unrestricted RDP access enables potential unauthorized remote desktop access.
|
||||
- **Detection Logic:**
|
||||
- Checks for ingress rules allowing TCP port 3389
|
||||
- Flags unrestricted IP ranges (None, 0.0.0.0/0, ::/0)
|
||||
- Reports security groups attached to NICs by default, or all security groups when `--scan-unused-services` is enabled
|
||||
|
||||
### 3. iaas_security_group_database_unrestricted
|
||||
|
||||
- **Severity:** High
|
||||
- **Description:** Detects security groups that allow unrestricted access to common database ports.
|
||||
- **Monitored Ports:**
|
||||
- MySQL: 3306
|
||||
- PostgreSQL: 5432
|
||||
- MongoDB: 27017
|
||||
- Redis: 6379
|
||||
- SQL Server: 1433
|
||||
- CouchDB: 5984
|
||||
- **Risk:** Unrestricted database access can lead to data breaches and unauthorized data access.
|
||||
|
||||
### 4. iaas_security_group_all_traffic_unrestricted
|
||||
|
||||
- **Severity:** Critical
|
||||
- **Description:** Detects security groups that allow all traffic from the internet.
|
||||
- **Detection Logic:**
|
||||
- Checks for rules with `port_range=None` (all ports)
|
||||
- Checks for rules with port range covering 0-65535 or 1-65535
|
||||
- Flags unrestricted IP ranges
|
||||
- Critical security misconfiguration requiring immediate remediation
|
||||
|
||||
### Important Implementation Notes
|
||||
|
||||
**Self-Referencing Security Group Rules:**
|
||||
|
||||
Security group rules with `remoteSecurityGroupId` set are automatically filtered out from unrestricted access checks. These rules only allow traffic from instances within the same security group (self-referencing), not from the internet, and are therefore not flagged as security risks.
|
||||
|
||||
**Rule Display Names:**
|
||||
|
||||
All findings include user-friendly rule descriptions when available. If a security group rule has a description field set (the name shown in the StackIT UI), it will be displayed in the finding message along with the rule ID:
|
||||
- With description: `'Allow SSH from office' (sgr-abc123)`
|
||||
- Without description: `'sgr-abc123'`
|
||||
|
||||
**Network Interface (NIC) Usage Filtering:**
|
||||
|
||||
The IaaS service lists project NICs and records the security group IDs attached to them. Checks use that signal to decide whether a security group is in use:
|
||||
|
||||
1. **Default behavior:** Report security groups attached to at least one NIC.
|
||||
2. **`--scan-unused-services`:** Report every security group, including unused ones.
|
||||
3. **FAIL logic:** Internet exposure is driven by security group rules that allow unrestricted source ranges, not by the presence of a public IP on the NIC.
|
||||
|
||||
**Unrestricted IP Ranges:**
|
||||
|
||||
The StackIT API represents "unrestricted" in two ways:
|
||||
- **`ip_range=null`**: No IP restriction specified (implicit unrestricted)
|
||||
- **`ip_range="0.0.0.0/0"` or `"::/0"`**: Explicitly configured to allow all IPs
|
||||
|
||||
Both are flagged as unrestricted. A `null` value is **more permissive** than an explicit range and applies to all protocols/ports if other fields are also `null`.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Python Version
|
||||
|
||||
- **Minimum:** Python 3.10+
|
||||
- **Reason:** The StackIT SDK requires Python 3.10 or higher
|
||||
|
||||
### Dependencies
|
||||
|
||||
The StackIT provider requires the following Python packages (automatically installed with Prowler):
|
||||
|
||||
- **stackit-core** (v0.2.0): Core SDK for StackIT API authentication and configuration
|
||||
- **stackit-iaas** (v1.4.0): IaaS service SDK for managing compute resources
|
||||
- **stackit-resourcemanager** (v0.8.0): Resource Manager SDK for fetching project metadata (e.g., project names)
|
||||
|
||||
These dependencies are defined in `pyproject.toml` and installed automatically with:
|
||||
|
||||
```bash
|
||||
poetry install
|
||||
```
|
||||
|
||||
**Note:** The `stackit-resourcemanager` package enables automatic retrieval of project names for display in reports. If this package is not available, Prowler will still function normally but project names will be empty in the output.
|
||||
|
||||
## Region Support
|
||||
|
||||
### Supported Regions
|
||||
|
||||
- **Available Regions:** `eu01` (Germany South) and `eu02` (Austria West)
|
||||
- **Default:** All scans use both `eu01` and `eu02` regions by default.
|
||||
|
||||
### Multi-Region Scanning
|
||||
|
||||
Prowler supports scanning multiple StackIT regions in a single execution. By default, it will scan all regions defined in the `stackit_regions_by_service.json` configuration file.
|
||||
|
||||
### CLI Argument
|
||||
|
||||
You can specify which regions to scan using the `--stackit-region` argument:
|
||||
|
||||
```bash
|
||||
# Scan only eu01
|
||||
prowler stackit --stackit-region eu01
|
||||
|
||||
# Scan both eu01 and eu02
|
||||
prowler stackit --stackit-region eu01 eu02
|
||||
```
|
||||
|
||||
### Implementation Details
|
||||
|
||||
- **Regional Clients:** Prowler generates a separate API client for each audited region.
|
||||
- **Service Iteration:** Each service (e.g., IaaS) iterates through the regional clients to fetch and audit resources.
|
||||
- **Identity Tracking:** The `audited_regions` are stored in the identity model for reporting.
|
||||
|
||||
### Future Enhancements
|
||||
|
||||
As StackIT adds more regions, they can be easily added to Prowler by updating the `prowler/providers/stackit/stackit_regions_by_service.json` file without requiring code changes.
|
||||
|
||||
## Command Examples
|
||||
|
||||
### Scan Specific Regions
|
||||
|
||||
Scan only the `eu01` region:
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY_PATH="$HOME/.stackit/sa-key.json"
|
||||
|
||||
prowler stackit \
|
||||
--stackit-project-id "your-project-id" \
|
||||
--stackit-region eu01
|
||||
```
|
||||
|
||||
Scan multiple regions:
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY_PATH="$HOME/.stackit/sa-key.json"
|
||||
|
||||
prowler stackit \
|
||||
--stackit-project-id "your-project-id" \
|
||||
--stackit-region eu01 eu02
|
||||
```
|
||||
|
||||
### Scan Specific Checks
|
||||
|
||||
Run only SSH unrestricted check:
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY_PATH="$HOME/.stackit/sa-key.json"
|
||||
|
||||
prowler stackit \
|
||||
--stackit-project-id "your-project-id" \
|
||||
--checks iaas_security_group_ssh_unrestricted
|
||||
```
|
||||
|
||||
### Scan All Security Group Checks
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY_PATH="$HOME/.stackit/sa-key.json"
|
||||
|
||||
prowler stackit \
|
||||
--stackit-project-id "your-project-id" \
|
||||
--services iaas
|
||||
```
|
||||
|
||||
### Output Formats
|
||||
|
||||
Generate JSON output:
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY_PATH="$HOME/.stackit/sa-key.json"
|
||||
|
||||
prowler stackit \
|
||||
--stackit-project-id "your-project-id" \
|
||||
--output-formats json
|
||||
```
|
||||
|
||||
Generate HTML report:
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY_PATH="$HOME/.stackit/sa-key.json"
|
||||
|
||||
prowler stackit \
|
||||
--stackit-project-id "your-project-id" \
|
||||
--output-formats html
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
### Current Limitations
|
||||
|
||||
1. **Single Project Scope**: Only one project can be scanned at a time
|
||||
2. **Service Coverage**: Only the IaaS service is currently implemented
|
||||
3. **Check Coverage**: Limited to security group network security checks (4 checks total)
|
||||
4. **No Compliance Frameworks**: Compliance framework mappings are not yet implemented
|
||||
|
||||
### Planned Enhancements
|
||||
|
||||
- Multi-project scanning capability
|
||||
- Additional IaaS checks (volume encryption, server public IP exposure, backup status)
|
||||
- Compliance framework mappings (CIS, custom StackIT best practices)
|
||||
- StackIT CLI remediation examples in metadata
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Authentication Errors
|
||||
|
||||
**Error:** `StackIT service account key was rejected`
|
||||
|
||||
**Solutions:**
|
||||
1. Re-issue the service account key in the StackIT Portal
|
||||
2. Verify the service account key file or inline JSON content is complete
|
||||
3. Check that the service account has the necessary permissions (`iaas.viewer` or `project.owner`)
|
||||
4. Ensure the service account key is provided through `STACKIT_SERVICE_ACCOUNT_KEY_PATH`, `STACKIT_SERVICE_ACCOUNT_KEY`, or the matching CLI arguments
|
||||
|
||||
**Error:** `StackIT credentials not found or are invalid`
|
||||
|
||||
**Solutions:**
|
||||
1. Ensure the project ID and one service account credential source are provided
|
||||
2. Check that credentials are set via environment variables or command-line arguments
|
||||
3. Verify there are no extra spaces or newlines in the credentials
|
||||
|
||||
**Error:** `Invalid StackIT project ID format`
|
||||
|
||||
**Solutions:**
|
||||
1. Verify the project ID is a valid UUID format: `12345678-1234-1234-1234-123456789abc`
|
||||
2. Copy the project ID directly from the StackIT Portal
|
||||
3. Ensure there are no extra spaces or quotes around the UUID
|
||||
|
||||
### API Connection Errors
|
||||
|
||||
**Error:** `Failed to connect to StackIT API`
|
||||
|
||||
**Solutions:**
|
||||
1. Check your internet connection
|
||||
2. Verify the StackIT API endpoint is accessible from your network
|
||||
3. Check if there are any firewall rules blocking HTTPS connections
|
||||
4. Review the full error message for specific API error codes
|
||||
|
||||
**Error:** `HTTP 403 Forbidden`
|
||||
|
||||
**Solutions:**
|
||||
1. Verify the service account has the correct permissions
|
||||
2. Ensure the project ID is correct and you have access to it
|
||||
3. Check that the service account is enabled (not disabled or expired)
|
||||
4. Verify the service account key has not been revoked
|
||||
|
||||
**Error:** `HTTP 404 Not Found`
|
||||
|
||||
**Solutions:**
|
||||
1. Verify the project ID exists and is correct
|
||||
2. Check that the IaaS service is enabled in your project
|
||||
3. Ensure you're using the correct region (eu01)
|
||||
|
||||
### Empty Results
|
||||
|
||||
**Issue:** No security groups or findings reported
|
||||
|
||||
**Solutions:**
|
||||
1. Verify that security groups exist in your project
|
||||
2. Check that the IaaS service is properly configured
|
||||
3. Ensure the service account has `iaas.viewer` permission
|
||||
4. Check Prowler logs for any API errors (use `--log-level DEBUG`)
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug logging for detailed troubleshooting:
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY_PATH="$HOME/.stackit/sa-key.json"
|
||||
|
||||
prowler stackit \
|
||||
--stackit-project-id "your-project-id" \
|
||||
--log-level DEBUG
|
||||
```
|
||||
|
||||
This will show:
|
||||
- API authentication details (with inline service account keys redacted)
|
||||
- Resource discovery progress
|
||||
- Security rule parsing details
|
||||
- Any API errors or warnings
|
||||
|
||||
## Specific Patterns in StackIT Services
|
||||
|
||||
The generic service pattern is described in [service page](/developer-guide/services#service-structure-and-initialisation). You can find all the currently implemented services in the following locations:
|
||||
|
||||
- Directly in the code, in location [`prowler/providers/stackit/services/`](https://github.com/prowler-cloud/prowler/tree/master/prowler/providers/stackit/services)
|
||||
- In the [Prowler Hub](https://hub.prowler.com/) for a more human-readable view.
|
||||
|
||||
The best reference to understand how to implement a new service is following the [service implementation documentation](/developer-guide/services#adding-a-new-service) and taking other StackIT services as reference.
|
||||
|
||||
### StackIT Service Common Patterns
|
||||
|
||||
- Services communicate with StackIT using the StackIT Python SDK, you can find the documentation [here](https://github.com/stackitcloud/stackit-sdk-python).
|
||||
- Service constructors receive a `StackitProvider` instance and use it to access credentials, identity, and configuration.
|
||||
- The provider builds StackIT SDK `Configuration` objects from the service account key path or inline key content.
|
||||
- Resource containers **must** be initialized in the constructor, typically as lists or dictionaries.
|
||||
- Do not manipulate `os.environ` for credentials inside services. Use the provider session and SDK configuration helpers.
|
||||
- All StackIT resources are represented as Pydantic `BaseModel` classes, providing type safety and structured access to resource attributes.
|
||||
- StackIT SDK calls are wrapped in try/except blocks, with specific handling for API errors, always logging errors.
|
||||
- **Centralized Error Handling**: Use `provider.handle_api_error(exception)` for consistent authentication error detection across all services.
|
||||
- **SDK Warning Suppression**: StackIT SDK prints deprecation warnings to stderr - use the `suppress_stderr()` context manager during SDK initialization and API calls.
|
||||
- **Unrestricted Access Detection**: In StackIT API, `None` values mean "allow all" (more permissive than explicit 0.0.0.0/0).
|
||||
- `protocol=None` → All protocols allowed
|
||||
- `ip_range=None` → All source IPs allowed (unrestricted!)
|
||||
- `port_range=None` → All ports allowed
|
||||
- `remote_security_group_id` set → Only allows traffic from the same security group (not unrestricted!)
|
||||
|
||||
### IaaS Service Specific Patterns
|
||||
|
||||
**Security Group Discovery:**
|
||||
```python
|
||||
# List all security groups
|
||||
security_groups = client.list_security_groups(
|
||||
project_id=self.project_id,
|
||||
region=region,
|
||||
)
|
||||
|
||||
# List network interfaces to determine security group usage
|
||||
nics = client.list_project_nics(
|
||||
project_id=self.project_id,
|
||||
region=region,
|
||||
)
|
||||
|
||||
# Checks report in-use security groups by default. Use --scan-unused-services
|
||||
# to include security groups that are not attached to any NIC.
|
||||
```
|
||||
|
||||
**Centralized Authentication Error Handling:**
|
||||
```python
|
||||
def _handle_api_call(self, api_function, *args, **kwargs):
|
||||
"""Wrapper for API calls with centralized error handling."""
|
||||
try:
|
||||
with suppress_stderr(): # Suppress SDK warnings
|
||||
return api_function(*args, **kwargs)
|
||||
except Exception as e:
|
||||
# Use centralized error handler from provider
|
||||
self.provider.handle_api_error(e) # Detects 401 and raises StackITInvalidTokenError
|
||||
```
|
||||
|
||||
**Unrestricted Access Detection:**
|
||||
```python
|
||||
def is_unrestricted(rule):
|
||||
"""Check if a rule allows unrestricted access."""
|
||||
# Filter out self-referencing rules
|
||||
if rule.remote_security_group_id is not None:
|
||||
return False
|
||||
# Check for unrestricted IP ranges
|
||||
return rule.ip_range is None or rule.ip_range in ["0.0.0.0/0", "::/0"]
|
||||
|
||||
def is_tcp(rule):
|
||||
"""Check if a rule applies to TCP protocol."""
|
||||
# None means all protocols (including TCP)
|
||||
return rule.protocol is None or rule.protocol.lower() in ["tcp", "all"]
|
||||
|
||||
def includes_port(rule, port):
|
||||
"""Check if a rule includes a specific port."""
|
||||
# None means all ports
|
||||
if rule.port_range is None:
|
||||
return True
|
||||
return rule.port_range.min <= port <= rule.port_range.max
|
||||
```
|
||||
|
||||
## Specific Patterns in StackIT Checks
|
||||
|
||||
The StackIT checks pattern is described in [checks page](/developer-guide/checks). You can find all the currently implemented checks:
|
||||
|
||||
- Directly in the code, within each service folder, each check has its own folder named after the name of the check. (e.g. [`prowler/providers/stackit/services/iaas/iaas_security_group_ssh_unrestricted/`](https://github.com/prowler-cloud/prowler/tree/master/prowler/providers/stackit/services/iaas/iaas_security_group_ssh_unrestricted))
|
||||
- In the [Prowler Hub](https://hub.prowler.com/) for a more human-readable view.
|
||||
|
||||
The best reference to understand how to implement a new check is following the [check creation documentation](/developer-guide/checks#creating-a-check) and taking other similar StackIT checks as reference.
|
||||
|
||||
### Check Report Class
|
||||
|
||||
The `CheckReportStackIT` class models a single finding for a StackIT resource in a check report. It is defined in [`prowler/lib/check/models.py`](https://github.com/prowler-cloud/prowler/blob/master/prowler/lib/check/models.py) and inherits from the generic `Check_Report` base class.
|
||||
|
||||
#### Purpose
|
||||
|
||||
`CheckReportStackIT` extends the base report structure with StackIT-specific fields, enabling detailed tracking of the resource, project, and location associated with each finding.
|
||||
|
||||
#### Constructor and Attribute Population
|
||||
|
||||
When you instantiate `CheckReportStackIT`, you must provide the check metadata and a resource object. The class will attempt to automatically populate its StackIT-specific attributes from the resource, using the following logic:
|
||||
|
||||
- **`resource_id`**:
|
||||
- Uses `resource.id` if present.
|
||||
- Otherwise, uses `resource.resource_id` if present.
|
||||
- Defaults to an empty string if none are available.
|
||||
|
||||
- **`resource_name`**:
|
||||
- Uses `resource.name` if present.
|
||||
- Defaults to an empty string if not available.
|
||||
|
||||
- **`project_id`**:
|
||||
- Uses `resource.project_id` if present.
|
||||
- Defaults to an empty string if not available (should be set in check logic).
|
||||
|
||||
- **`location`**:
|
||||
- Uses `resource.region` if present.
|
||||
- Otherwise, uses `resource.location` if present.
|
||||
- Defaults to an empty string if not available.
|
||||
|
||||
If the resource object does not contain the required attributes, you must set them manually in the check logic.
|
||||
|
||||
Other attributes are inherited from the `Check_Report` class, from which you **always** have to set the `status` and `status_extended` attributes in the check logic.
|
||||
|
||||
#### Example Usage
|
||||
|
||||
```python
|
||||
from prowler.lib.check.models import CheckReportStackIT
|
||||
|
||||
report = CheckReportStackIT(
|
||||
metadata=self.metadata(),
|
||||
resource=security_group
|
||||
)
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Security group {security_group.name} allows unrestricted SSH access from the internet."
|
||||
report.resource_id = security_group.id
|
||||
report.resource_name = security_group.name
|
||||
report.project_id = security_group.project_id
|
||||
report.location = security_group.region
|
||||
```
|
||||
|
||||
### Common Check Pattern
|
||||
|
||||
```python
|
||||
from prowler.lib.check.models import Check, CheckReportStackIT
|
||||
from prowler.providers.stackit.services.iaas.iaas_client import iaas_client
|
||||
|
||||
class iaas_security_group_ssh_unrestricted(Check):
|
||||
"""Check if IaaS security groups allow unrestricted SSH access."""
|
||||
|
||||
def execute(self):
|
||||
findings = []
|
||||
|
||||
for security_group in iaas_client.security_groups:
|
||||
if not (iaas_client.scan_unused_services or security_group.in_use):
|
||||
continue
|
||||
|
||||
report = CheckReportStackIT(
|
||||
metadata=self.metadata(),
|
||||
resource=security_group
|
||||
)
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"Security group {security_group.name} does not allow unrestricted SSH access."
|
||||
|
||||
# Check each rule
|
||||
for rule in security_group.rules:
|
||||
if (rule.is_ingress() and
|
||||
rule.is_tcp() and
|
||||
rule.includes_port(22) and
|
||||
rule.is_unrestricted()):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"Security group {security_group.name} allows unrestricted SSH access from the internet."
|
||||
break
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
### Official StackIT Documentation
|
||||
|
||||
- **StackIT Portal**: [https://portal.stackit.cloud/](https://portal.stackit.cloud/)
|
||||
- **StackIT Documentation**: [https://docs.stackit.cloud/](https://docs.stackit.cloud/)
|
||||
- **StackIT API Documentation**: [https://docs.api.eu01.stackit.cloud/](https://docs.api.eu01.stackit.cloud/)
|
||||
|
||||
### Python SDK
|
||||
|
||||
- **StackIT Python SDK (GitHub)**: [https://github.com/stackitcloud/stackit-sdk-python](https://github.com/stackitcloud/stackit-sdk-python)
|
||||
- **stackit-core (PyPI)**: [https://pypi.org/project/stackit-core/](https://pypi.org/project/stackit-core/)
|
||||
- **stackit-iaas (PyPI)**: [https://pypi.org/project/stackit-iaas/](https://pypi.org/project/stackit-iaas/)
|
||||
- **IaaS Models**: [https://github.com/stackitcloud/stackit-sdk-python/tree/main/services/iaas/src/stackit/iaas/models](https://github.com/stackitcloud/stackit-sdk-python/tree/main/services/iaas/src/stackit/iaas/models)
|
||||
|
||||
### Prowler Resources
|
||||
|
||||
- **Provider Implementation**: [`prowler/providers/stackit/`](https://github.com/prowler-cloud/prowler/tree/master/prowler/providers/stackit/)
|
||||
- **IaaS Service**: [`prowler/providers/stackit/services/iaas/`](https://github.com/prowler-cloud/prowler/tree/master/prowler/providers/stackit/services/iaas/)
|
||||
- **Prowler Hub**: [https://hub.prowler.com/](https://hub.prowler.com/)
|
||||
- **GitHub Issues**: [https://github.com/prowler-cloud/prowler/issues](https://github.com/prowler-cloud/prowler/issues)
|
||||
|
||||
## Contributing
|
||||
|
||||
If you'd like to contribute to the StackIT provider:
|
||||
|
||||
1. **Add New Checks**: Follow the [check creation guide](/developer-guide/checks#creating-a-check) and use existing StackIT checks as templates
|
||||
2. **Enhance Services**: Implement additional IaaS resource discovery or add new services
|
||||
3. **Improve Documentation**: Add metadata enhancements, CLI remediation examples, or Terraform code samples
|
||||
4. **Report Issues**: Submit bug reports or feature requests on [GitHub](https://github.com/prowler-cloud/prowler/issues)
|
||||
|
||||
### Quick Start for Contributors
|
||||
|
||||
1. **Install dependencies**: `poetry install` (includes stackit-core and stackit-iaas)
|
||||
2. **Set credentials**: Export `STACKIT_SERVICE_ACCOUNT_KEY_PATH` and `STACKIT_PROJECT_ID`
|
||||
3. **Run checks**: `prowler stackit`
|
||||
4. **View code**: Start in `prowler/providers/stackit/`
|
||||
5. **Add checks**: Create new check directories under `services/iaas/`
|
||||
6. **Run tests**: `poetry run pytest tests/providers/stackit/ -v`
|
||||
|
||||
### Code Quality Standards
|
||||
|
||||
The StackIT provider should follow the same quality expectations as the rest of the Prowler SDK:
|
||||
|
||||
- Keep service and check logic covered by unit tests.
|
||||
- Redact inline service account keys from generated output.
|
||||
- Keep documentation aligned with the implemented services and checks.
|
||||
- Follow existing provider, service, and check patterns before adding StackIT-specific abstractions.
|
||||
+19
-3
@@ -124,8 +124,8 @@
|
||||
"user-guide/tutorials/prowler-app-rbac",
|
||||
"user-guide/tutorials/prowler-app-multi-tenant",
|
||||
"user-guide/tutorials/prowler-app-api-keys",
|
||||
"user-guide/tutorials/prowler-app-import-findings",
|
||||
"user-guide/tutorials/prowler-app-alerts",
|
||||
"user-guide/tutorials/prowler-import-findings",
|
||||
"user-guide/tutorials/prowler-alerts",
|
||||
{
|
||||
"group": "Mutelist",
|
||||
"expanded": true,
|
||||
@@ -339,6 +339,13 @@
|
||||
"user-guide/providers/scaleway/authentication"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "StackIT",
|
||||
"pages": [
|
||||
"user-guide/providers/stackit/getting-started-stackit",
|
||||
"user-guide/providers/stackit/authentication"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Vercel",
|
||||
"pages": [
|
||||
@@ -401,7 +408,8 @@
|
||||
"developer-guide/kubernetes-details",
|
||||
"developer-guide/m365-details",
|
||||
"developer-guide/github-details",
|
||||
"developer-guide/llm-details"
|
||||
"developer-guide/llm-details",
|
||||
"developer-guide/stackit-details"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -576,6 +584,14 @@
|
||||
{
|
||||
"source": "/contact",
|
||||
"destination": "/support"
|
||||
},
|
||||
{
|
||||
"source": "/user-guide/tutorials/prowler-app-import-findings",
|
||||
"destination": "/user-guide/tutorials/prowler-import-findings"
|
||||
},
|
||||
{
|
||||
"source": "/user-guide/tutorials/prowler-app-alerts",
|
||||
"destination": "/user-guide/tutorials/prowler-alerts"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -40,12 +40,6 @@ To install Prowler as a Python package, use `Python >= 3.10, <= 3.12`. Prowler i
|
||||
pip install prowler
|
||||
prowler -v
|
||||
```
|
||||
|
||||
To upgrade Prowler to the latest version:
|
||||
|
||||
``` bash
|
||||
pip install --upgrade prowler
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Docker">
|
||||
_Requirements_:
|
||||
@@ -170,6 +164,68 @@ To install Prowler as a Python package, use `Python >= 3.10, <= 3.12`. Prowler i
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Updating Prowler CLI
|
||||
|
||||
Upgrade Prowler CLI to the latest release using the same method chosen for installation:
|
||||
|
||||
<Tabs>
|
||||
<Tab title="pipx">
|
||||
```bash
|
||||
pipx upgrade prowler
|
||||
prowler -v
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="pip">
|
||||
```bash
|
||||
pip install --upgrade prowler
|
||||
prowler -v
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Docker">
|
||||
Pull the desired image tag to fetch the latest version:
|
||||
|
||||
```bash
|
||||
docker pull toniblyx/prowler:latest
|
||||
```
|
||||
|
||||
<Note>
|
||||
Replace `latest` with a specific release tag (for example, `stable` or `<x.y.z>`) to pin a version. Refer to the [Container Versions](#container-versions) section for the full list of available tags.
|
||||
</Note>
|
||||
</Tab>
|
||||
<Tab title="GitHub">
|
||||
Pull the latest changes and sync the environment:
|
||||
|
||||
```bash
|
||||
cd prowler
|
||||
git pull
|
||||
uv sync
|
||||
uv run python prowler-cli.py -v
|
||||
```
|
||||
|
||||
<Note>
|
||||
To upgrade to a specific release, check out the corresponding tag before syncing: `git checkout <x.y.z>`.
|
||||
</Note>
|
||||
</Tab>
|
||||
<Tab title="Brew">
|
||||
```bash
|
||||
brew upgrade prowler
|
||||
prowler -v
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="CloudShell">
|
||||
Both AWS CloudShell and Azure CloudShell install Prowler with `pipx`, so the upgrade command is the same:
|
||||
|
||||
```bash
|
||||
pipx upgrade prowler
|
||||
prowler -v
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Note>
|
||||
To install a specific version instead of the latest release, pin it explicitly. For example, with `pipx`: `pipx install prowler==<x.y.z>`, or with `pip`: `pip install prowler==<x.y.z>`. The available releases are listed in the [Releases GitHub section](https://github.com/prowler-cloud/prowler/releases).
|
||||
</Note>
|
||||
|
||||
## Container Versions
|
||||
|
||||
The available versions of Prowler CLI are the following:
|
||||
|
||||
@@ -141,6 +141,45 @@ Choose one of the following installation methods:
|
||||
|
||||
---
|
||||
|
||||
## Updating Prowler MCP Server
|
||||
|
||||
When running Prowler MCP Server locally ("Option 2: Run Locally"), upgrade to the latest version using the same method chosen for installation. The hosted server (`https://mcp.prowler.com/mcp`) is always kept up to date by Prowler and requires no action.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Docker">
|
||||
Pull the latest image and restart the container:
|
||||
|
||||
```bash
|
||||
docker pull prowlercloud/prowler-mcp
|
||||
```
|
||||
|
||||
<Note>
|
||||
Recreate any running container after pulling the new image so the updated version takes effect.
|
||||
</Note>
|
||||
</Tab>
|
||||
<Tab title="From Source">
|
||||
Pull the latest changes and sync the dependencies:
|
||||
|
||||
```bash
|
||||
cd prowler/mcp_server
|
||||
git pull
|
||||
uv sync
|
||||
uv run prowler-mcp --help
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Build Docker Image">
|
||||
Pull the latest source and rebuild the image:
|
||||
|
||||
```bash
|
||||
cd prowler/mcp_server
|
||||
git pull
|
||||
docker build -t prowler-mcp .
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Command Line Options
|
||||
|
||||
The Prowler MCP Server supports the following command-line arguments:
|
||||
|
||||
@@ -36,6 +36,7 @@ Prowler supports a wide range of providers organized by category:
|
||||
| [OpenStack](/user-guide/providers/openstack/getting-started-openstack) | Official | Projects | UI, API, CLI |
|
||||
| [Oracle Cloud](/user-guide/providers/oci/getting-started-oci) | Official | Tenancies / Compartments | UI, API, CLI |
|
||||
| [Scaleway](/user-guide/providers/scaleway/getting-started-scaleway) | [Contact us](https://prowler.com/contact) | Organizations | CLI |
|
||||
| [StackIT](/user-guide/providers/stackit/getting-started-stackit) | [Contact us](https://prowler.com/contact) | Projects | CLI |
|
||||
|
||||
### Infrastructure as Code Providers
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ title: 'Run Prowler in CI/CD and Send Findings to Prowler Cloud'
|
||||
For new projects, use the official [Prowler GitHub Action](/user-guide/tutorials/prowler-app-github-action) — a Docker-based reusable action that runs scans, optionally pushes findings to Prowler Cloud, and uploads SARIF results to GitHub Code Scanning. The GitHub Actions examples below document the legacy pip-based flow.
|
||||
</Warning>
|
||||
|
||||
This cookbook demonstrates how to integrate Prowler into CI/CD pipelines so that security scans run automatically and findings are sent to Prowler Cloud via [Import Findings](/user-guide/tutorials/prowler-app-import-findings). Examples cover GitHub Actions and GitLab CI.
|
||||
This cookbook demonstrates how to integrate Prowler into CI/CD pipelines so that security scans run automatically and findings are sent to Prowler Cloud via [Import Findings](/user-guide/tutorials/prowler-import-findings). Examples cover GitHub Actions and GitLab CI.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -19,7 +19,7 @@ This cookbook demonstrates how to integrate Prowler into CI/CD pipelines so that
|
||||
|
||||
Prowler CLI provides the `--push-to-cloud` flag, which uploads scan results directly to Prowler Cloud after a scan completes. Combined with the `PROWLER_CLOUD_API_KEY` environment variable, this enables fully automated ingestion without manual file uploads.
|
||||
|
||||
For full details on the flag and API, refer to the [Import Findings](/user-guide/tutorials/prowler-app-import-findings) documentation.
|
||||
For full details on the flag and API, refer to the [Import Findings](/user-guide/tutorials/prowler-import-findings) documentation.
|
||||
|
||||
<Note>
|
||||
The examples in this guide use AWS as the target provider, but the same approach applies to any provider supported by Prowler (Azure, GCP, Kubernetes, and others). Replace `prowler aws` with the desired provider command (e.g., `prowler gcp`, `prowler azure`) and configure the corresponding credentials in the CI/CD environment.
|
||||
@@ -195,7 +195,7 @@ By default, Prowler exits with a non-zero code when it finds failing checks. Thi
|
||||
* **GitLab CI**: Add `allow_failure: true` to the job
|
||||
|
||||
<Note>
|
||||
Ingestion failures (e.g., network issues reaching Prowler Cloud) do not affect the Prowler exit code. The scan completes normally and only a warning is emitted. See [Import Findings troubleshooting](/user-guide/tutorials/prowler-app-import-findings#troubleshooting) for details.
|
||||
Ingestion failures (e.g., network issues reaching Prowler Cloud) do not affect the Prowler exit code. The scan completes normally and only a warning is emitted. See [Import Findings troubleshooting](/user-guide/tutorials/prowler-import-findings#troubleshooting) for details.
|
||||
</Note>
|
||||
|
||||
### Caching Prowler Installation
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
title: 'Run Kubernetes In-Cluster and Send Findings to Prowler Cloud'
|
||||
---
|
||||
|
||||
This cookbook walks through deploying Prowler inside a Kubernetes cluster on a recurring schedule and automatically sending findings to Prowler Cloud via [Import Findings](/user-guide/tutorials/prowler-app-import-findings). By the end, security scan results from the cluster appear in Prowler Cloud without any manual file uploads.
|
||||
This cookbook walks through deploying Prowler inside a Kubernetes cluster on a recurring schedule and automatically sending findings to Prowler Cloud via [Import Findings](/user-guide/tutorials/prowler-import-findings). By the end, security scan results from the cluster appear in Prowler Cloud without any manual file uploads.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -181,7 +181,7 @@ Once the job completes and findings are pushed:
|
||||
2. Open the "Scans" section to verify the ingestion job status
|
||||
3. Browse findings under the Kubernetes provider
|
||||
|
||||
For details on the ingestion workflow and status tracking, refer to the [Import Findings](/user-guide/tutorials/prowler-app-import-findings) documentation.
|
||||
For details on the ingestion workflow and status tracking, refer to the [Import Findings](/user-guide/tutorials/prowler-import-findings) documentation.
|
||||
|
||||
## Tips and Troubleshooting
|
||||
|
||||
@@ -204,4 +204,4 @@ For details on the ingestion workflow and status tracking, refer to the [Import
|
||||
--namespace prowler-ns
|
||||
```
|
||||
|
||||
* **Failed uploads**: If the push to Prowler Cloud fails, the scan still completes and findings are saved locally in the container. Check the [Import Findings troubleshooting section](/user-guide/tutorials/prowler-app-import-findings#troubleshooting) for common error messages.
|
||||
* **Failed uploads**: If the push to Prowler Cloud fails, the scan still completes and findings are saved locally in the container. Check the [Import Findings troubleshooting section](/user-guide/tutorials/prowler-import-findings#troubleshooting) for common error messages.
|
||||
|
||||
@@ -18,7 +18,7 @@ Prowler requests the following read-only OAuth 2.0 scopes:
|
||||
| `https://www.googleapis.com/auth/admin.directory.domain.readonly` | Read access to domain information |
|
||||
| `https://www.googleapis.com/auth/admin.directory.customer.readonly` | Read access to customer information (Customer ID) |
|
||||
| `https://www.googleapis.com/auth/admin.directory.orgunit.readonly` | Read access to organizational unit hierarchy (identifies the root OU for policy filtering) |
|
||||
| `https://www.googleapis.com/auth/cloud-identity.policies.readonly` | Read access to domain-level application policies (required for Calendar, Gmail, Chat, and Drive service checks) |
|
||||
| `https://www.googleapis.com/auth/cloud-identity.policies.readonly` | Read access to domain-level application policies (required for Calendar, Chat, Drive, Gmail, Groups, Marketplace, Security, and Sites service checks) |
|
||||
| `https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly` | Read access to admin roles and role assignments |
|
||||
|
||||
<Warning>
|
||||
@@ -40,7 +40,7 @@ In the [Google Cloud Console](https://console.cloud.google.com), select the targ
|
||||
| API | Required For |
|
||||
|-----|--------------|
|
||||
| **Admin SDK API** | Directory service checks (users, roles, domains) |
|
||||
| **Cloud Identity API** | Calendar, Gmail, Chat, and Drive service checks (domain-level application policies) |
|
||||
| **Cloud Identity API** | All service checks except Directory (domain-level application policies) |
|
||||
|
||||
For each API:
|
||||
|
||||
@@ -49,7 +49,7 @@ For each API:
|
||||
3. Click **Enable**
|
||||
|
||||
<Note>
|
||||
Both APIs must be enabled in the same GCP project that hosts the Service Account. Calendar, Gmail, Chat, and Drive checks will return no findings if the Cloud Identity API is not enabled.
|
||||
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.
|
||||
</Note>
|
||||
|
||||
### Step 3: Create a Service Account
|
||||
@@ -178,7 +178,7 @@ If Prowler connects but returns empty results or permission errors for specific
|
||||
|
||||
### Policy API Checks Return No Findings
|
||||
|
||||
If the Directory checks run successfully but the Calendar, Gmail, Chat, or Drive checks return no findings, the Cloud Identity Policy API is not reachable for this Service Account. Verify:
|
||||
If the Directory checks run successfully but other service checks (Calendar, Chat, Drive, Gmail, Groups, Marketplace, Security, Sites) return no findings, the Cloud Identity Policy API is not reachable for this Service Account. Verify:
|
||||
|
||||
- The **Cloud Identity API** is enabled in the GCP project hosting the Service Account (Step 2)
|
||||
- The scope `https://www.googleapis.com/auth/cloud-identity.policies.readonly` is included in the Domain-Wide Delegation OAuth scopes list in the Admin Console (Step 5)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: 'StackIT Authentication'
|
||||
---
|
||||
|
||||
Prowler authenticates with StackIT using a **service account key file**. The StackIT SDK signs the RSA challenge in the key file and mints/refreshes access tokens internally for the life of the scan, so no manual token rotation is needed.
|
||||
|
||||
## Service Account Key
|
||||
|
||||
StackIT uses RSA key-pair based service account keys. They are issued once, must be stored securely, and are read by the SDK on every scan to mint short-lived access tokens transparently.
|
||||
|
||||
### Option 1: Create the Key via the StackIT Portal
|
||||
|
||||
1. Open the [StackIT Portal](https://portal.stackit.cloud/) and select your project.
|
||||
2. In the left sidebar, click **Service Accounts**.
|
||||
3. Create a service account if you do not have one already. Assign:
|
||||
- `iaas.viewer` for the IaaS security group checks currently shipped, or
|
||||
- `project.owner` if you want to cover any future service Prowler adds.
|
||||
4. Open the service account and go to **Service Account Keys**.
|
||||
5. Click **Create key** and choose **STACKIT-generated key pair** (recommended). Download the resulting JSON file and store it securely (for example, `~/.stackit/sa-key.json`). The private material is only shown once.
|
||||
|
||||
### Option 2: Create the Key via the StackIT CLI
|
||||
|
||||
```bash
|
||||
# Install the StackIT CLI from https://github.com/stackitcloud/stackit-cli first
|
||||
stackit service-account key create --email my-service-account@example.com
|
||||
```
|
||||
|
||||
## Project ID
|
||||
|
||||
Your StackIT project ID is a UUID. You can find it in:
|
||||
|
||||
1. The portal URL when viewing the project: `https://portal.stackit.cloud/projects/{PROJECT_ID}/...`
|
||||
2. The project settings page
|
||||
3. `stackit project list`
|
||||
|
||||
## Passing Credentials to Prowler
|
||||
|
||||
You can give Prowler either the **path** to the key file on disk or the **inline JSON content** of the key. Both go through the same StackIT SDK flow and refresh access tokens internally.
|
||||
|
||||
### Option A: Key File Path (workstation, persistent agents)
|
||||
|
||||
Recommended when the key is stored on disk.
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY_PATH="$HOME/.stackit/sa-key.json"
|
||||
export STACKIT_PROJECT_ID="12345678-1234-1234-1234-123456789abc"
|
||||
|
||||
prowler stackit
|
||||
```
|
||||
|
||||
Or as CLI flags:
|
||||
|
||||
```bash
|
||||
prowler stackit \
|
||||
--stackit-service-account-key-path ~/.stackit/sa-key.json \
|
||||
--stackit-project-id 12345678-1234-1234-1234-123456789abc
|
||||
```
|
||||
|
||||
<Note>
|
||||
Keep the key file outside of source control and lock it down with `chmod 600 ~/.stackit/sa-key.json`. Anyone with the JSON can mint access tokens for the service account.
|
||||
</Note>
|
||||
|
||||
### Option B: Inline Key Content (CI/CD, secret managers)
|
||||
|
||||
Recommended when the key is fetched at run time from a secret manager (GitHub Actions secret, AWS Secrets Manager, HashiCorp Vault, etc.) and you do not want to write it to disk.
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY="$(vault kv get -field=key stackit/sa)"
|
||||
export STACKIT_PROJECT_ID="12345678-1234-1234-1234-123456789abc"
|
||||
|
||||
prowler stackit
|
||||
```
|
||||
|
||||
<Note>
|
||||
Prefer the `STACKIT_SERVICE_ACCOUNT_KEY` environment variable over the matching CLI flag (`--stackit-service-account-key`); passing the secret on the command line leaks it through process listings and shell history.
|
||||
</Note>
|
||||
|
||||
When both the inline content and a key path are set, the inline content wins.
|
||||
|
||||
## Credential Lookup Order
|
||||
|
||||
Prowler resolves credentials in this order:
|
||||
|
||||
1. CLI arguments: `--stackit-service-account-key`, `--stackit-service-account-key-path`, `--stackit-project-id`
|
||||
2. Environment variables: `STACKIT_SERVICE_ACCOUNT_KEY`, `STACKIT_SERVICE_ACCOUNT_KEY_PATH`, `STACKIT_PROJECT_ID`
|
||||
|
||||
When both the inline key and the key file path are set, the inline content takes precedence.
|
||||
|
||||
## Token Lifetime
|
||||
|
||||
Access tokens are minted on demand by the SDK from the key file and refreshed before they expire. There is nothing to rotate while Prowler is running.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Likely Cause | Fix |
|
||||
|---------|--------------|-----|
|
||||
| `401 Unauthorized` during scan | Key file is missing fields, the public key is no longer registered, or the key was revoked | Re-issue the service account key in the StackIT portal and update `STACKIT_SERVICE_ACCOUNT_KEY_PATH` |
|
||||
| `403 Forbidden` during scan | Service account lacks role on the project | Re-check role assignment in the StackIT portal; `iaas.viewer` is the minimum for the shipped IaaS checks |
|
||||
| `StackIT project ID must be a valid UUID` | The project ID is not in UUID format | Copy the UUID from the portal URL or `stackit project list` |
|
||||
| `StackIT service account credentials are required` | None of the four credential inputs is set | Export `STACKIT_SERVICE_ACCOUNT_KEY_PATH` or `STACKIT_SERVICE_ACCOUNT_KEY` (or use their CLI counterparts) before running Prowler |
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
title: 'Getting Started With StackIT'
|
||||
---
|
||||
|
||||
Prowler supports [StackIT](https://www.stackit.de/) from the CLI. This guide walks you through the requirements and how to run scans.
|
||||
|
||||
<Note>
|
||||
StackIT support in Prowler is community-maintained. For commercial support or to request additional service coverage, [contact us](https://prowler.com/contact).
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before running Prowler with the StackIT provider, ensure you have:
|
||||
|
||||
1. A StackIT account with at least one project
|
||||
2. A StackIT service account key file with permissions on the project (`iaas.viewer` is enough for the currently shipped IaaS checks; `project.owner` works for any future service). See the [Authentication guide](/user-guide/providers/stackit/authentication) for the full setup.
|
||||
3. Access to Prowler CLI (see [Installation](/getting-started/installation/prowler-cli))
|
||||
|
||||
## Prowler CLI
|
||||
|
||||
### Step 1: Point Prowler at the Service Account Key
|
||||
|
||||
Prowler authenticates with a StackIT service account key. The SDK signs the RSA challenge in the key and refreshes access tokens internally for the life of the scan, so there is no manual token rotation.
|
||||
|
||||
**On a workstation or persistent agent** (key on disk):
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY_PATH="$HOME/.stackit/sa-key.json"
|
||||
export STACKIT_PROJECT_ID="12345678-1234-1234-1234-123456789abc"
|
||||
```
|
||||
|
||||
**In CI/CD** (key in a secret manager, never written to disk):
|
||||
|
||||
```bash
|
||||
export STACKIT_SERVICE_ACCOUNT_KEY="$(vault kv get -field=key stackit/sa)"
|
||||
export STACKIT_PROJECT_ID="12345678-1234-1234-1234-123456789abc"
|
||||
```
|
||||
|
||||
CLI flags work too:
|
||||
|
||||
```bash
|
||||
prowler stackit \
|
||||
--stackit-service-account-key-path ~/.stackit/sa-key.json \
|
||||
--stackit-project-id 12345678-1234-1234-1234-123456789abc
|
||||
```
|
||||
|
||||
<Note>
|
||||
For the inline key, prefer the `STACKIT_SERVICE_ACCOUNT_KEY` env var over the matching CLI flag; passing the secret on the command line leaks it through process listings and shell history.
|
||||
|
||||
Keep the key file outside of source control and lock it down with `chmod 600 ~/.stackit/sa-key.json`. Anyone with the JSON can mint access tokens for the service account.
|
||||
</Note>
|
||||
|
||||
### Step 2: Run Your First Scan
|
||||
|
||||
```bash
|
||||
prowler stackit
|
||||
```
|
||||
|
||||
Prowler will discover and audit the project's IaaS security groups across the available StackIT regions.
|
||||
|
||||
**Scan specific regions:**
|
||||
|
||||
```bash
|
||||
prowler stackit --stackit-region eu01 eu02
|
||||
```
|
||||
|
||||
**Run specific security checks:**
|
||||
|
||||
```bash
|
||||
prowler stackit --checks iaas_security_group_ssh_unrestricted
|
||||
|
||||
# List all available checks
|
||||
prowler stackit --list-checks
|
||||
```
|
||||
|
||||
**Filter by check severity:**
|
||||
|
||||
```bash
|
||||
prowler stackit --severity critical high
|
||||
```
|
||||
|
||||
**Generate specific output formats:**
|
||||
|
||||
```bash
|
||||
# JSON only
|
||||
prowler stackit --output-modes json
|
||||
|
||||
# CSV and HTML
|
||||
prowler stackit --output-modes csv html
|
||||
|
||||
# Custom output directory
|
||||
prowler stackit --output-directory /path/to/reports/
|
||||
```
|
||||
|
||||
**Use a mutelist to suppress findings:**
|
||||
|
||||
```yaml
|
||||
# mutelist.yaml
|
||||
Mutelist:
|
||||
Accounts:
|
||||
"12345678-1234-1234-1234-123456789abc":
|
||||
Checks:
|
||||
iaas_security_group_ssh_unrestricted:
|
||||
Regions:
|
||||
- "*"
|
||||
Resources:
|
||||
- "test-sg-id"
|
||||
Tags: []
|
||||
```
|
||||
|
||||
```bash
|
||||
prowler stackit --mutelist-file mutelist.yaml
|
||||
```
|
||||
|
||||
### Step 3: Review the Results
|
||||
|
||||
Prowler outputs findings to the console and writes reports to the `output/` directory by default:
|
||||
|
||||
- CSV: `output/prowler-output-stackit-{project_id}-{timestamp}.csv`
|
||||
- JSON: `output/prowler-output-stackit-{project_id}-{timestamp}.json`
|
||||
- HTML: `output/prowler-output-stackit-{project_id}-{timestamp}.html`
|
||||
|
||||
## Supported StackIT Services
|
||||
|
||||
| Service | StackIT API | Description | Example Checks |
|
||||
|---------|-------------|-------------|----------------|
|
||||
| **IaaS** | `iaas` | Virtual machines, network interfaces, security groups | `iaas_security_group_ssh_unrestricted`, `iaas_security_group_rdp_unrestricted`, `iaas_security_group_database_unrestricted`, `iaas_security_group_all_traffic_unrestricted` |
|
||||
|
||||
Additional services will be added in future releases. Track progress in the [Prowler release notes](https://github.com/prowler-cloud/prowler/releases).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Authentication Errors
|
||||
|
||||
If the scan fails with a 401 error, the service account key is no longer valid (revoked, rotated or the key file is incomplete). Re-issue the key in the [StackIT portal](https://portal.stackit.cloud/) and update `STACKIT_SERVICE_ACCOUNT_KEY_PATH`.
|
||||
|
||||
### Permission Errors
|
||||
|
||||
If checks fail with a 403 error, the service account is missing the required role on the project. Re-check the role assignment in the StackIT portal (`iaas.viewer` is the minimum for the shipped IaaS checks).
|
||||
|
||||
For detailed setup steps, see the [Authentication guide](/user-guide/providers/stackit/authentication).
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
Alerts notify recipients by email when security findings match saved filter conditions. Use Alerts to track high-priority findings, monitor specific providers or services, and keep teams informed about scan results that match defined criteria.
|
||||
|
||||
<Note>
|
||||
This feature is available exclusively in **Prowler Cloud** with a paid subscription.
|
||||
This feature is available exclusively in **Prowler Cloud** and **Prowler Enterprise** with a [paid subscription](https://prowler.com/pricing).
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
@@ -18,7 +18,7 @@ Source: [`prowler-cloud/prowler`](https://github.com/prowler-cloud/prowler) · M
|
||||
| `provider` | yes | — | Cloud provider to scan (`aws`, `azure`, `gcp`, `github`, `kubernetes`, `iac`, `cloudflare`, etc.) |
|
||||
| `image-tag` | no | `stable` | Docker image tag — `stable` (latest release), `latest` (master, not stable), or `<x.y.z>` (pinned). See [available tags](https://hub.docker.com/r/prowlercloud/prowler/tags). |
|
||||
| `output-formats` | no | `json-ocsf` | Output format(s) for scan results. Space-separated (e.g. `sarif json-ocsf`) |
|
||||
| `push-to-cloud` | no | `false` | Push findings to [Prowler Cloud](/user-guide/tutorials/prowler-app-import-findings). When `true`, `PROWLER_CLOUD_API_KEY` is auto-forwarded |
|
||||
| `push-to-cloud` | no | `false` | Push findings to [Prowler Cloud](/user-guide/tutorials/prowler-import-findings). When `true`, `PROWLER_CLOUD_API_KEY` is auto-forwarded |
|
||||
| `flags` | no | `""` | Additional CLI flags (e.g. `--severity critical high`). Values with spaces can be quoted: `--resource-tag 'Environment=My Server'` |
|
||||
| `extra-env` | no | `""` | Space-, newline-, or comma-separated list of env var **names** to forward to the container (see [Authentication](#authentication)) |
|
||||
| `upload-sarif` | no | `false` | Upload SARIF results to GitHub Code Scanning |
|
||||
@@ -43,7 +43,7 @@ Source: [`prowler-cloud/prowler`](https://github.com/prowler-cloud/prowler) · M
|
||||
|
||||
### Push findings to Prowler Cloud
|
||||
|
||||
Send scan results directly to [Prowler Cloud](/user-guide/tutorials/prowler-app-import-findings) for centralized visibility, compliance tracking, and team collaboration.
|
||||
Send scan results directly to [Prowler Cloud](/user-guide/tutorials/prowler-import-findings) for centralized visibility, compliance tracking, and team collaboration.
|
||||
|
||||
```yaml
|
||||
- uses: prowler-cloud/prowler@5.25
|
||||
|
||||
@@ -239,7 +239,7 @@ To grant all administrative permissions, select the **Grant all admin permission
|
||||
|
||||
The following permissions are available exclusively in **Prowler Cloud**:
|
||||
|
||||
**Manage Ingestions:** Submit and manage findings ingestion jobs via the API. Required to upload OCSF scan results using the `--push-to-cloud` CLI flag or the ingestion endpoints. See [Import Findings](/user-guide/tutorials/prowler-app-import-findings) for details.
|
||||
**Manage Ingestions:** Submit and manage findings ingestion jobs via the API. Required to upload OCSF scan results using the `--push-to-cloud` CLI flag or the ingestion endpoints. See [Import Findings](/user-guide/tutorials/prowler-import-findings) for details.
|
||||
|
||||
**Manage Billing:** Access and manage billing settings, subscription plans, and payment methods.
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { VersionBadge } from "/snippets/version-badge.mdx"
|
||||
Findings Ingestion enables uploading OCSF (Open Cybersecurity Schema Framework) scan results to Prowler Cloud. This feature supports importing findings from Prowler CLI output files that use the [Detection Finding](https://schema.ocsf.io/classes/detection_finding) class.
|
||||
|
||||
<Note>
|
||||
This feature is available exclusively in **Prowler Cloud** with a paid subscription.
|
||||
This feature is available exclusively in **Prowler Cloud** and **Prowler Enterprise** with a [paid subscription](https://prowler.com/pricing).
|
||||
</Note>
|
||||
|
||||
## OCSF Detection Finding format
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **Skills Reference**: See [`prowler-mcp`](../skills/prowler-mcp/SKILL.md)
|
||||
|
||||
### Auto-invoke Skills
|
||||
## Auto-invoke Skills
|
||||
|
||||
When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
|
||||
@@ -68,7 +68,7 @@ Python 3.12+ | FastMCP 2.13.1 | httpx (async) | Pydantic | uv
|
||||
|
||||
## PROJECT STRUCTURE
|
||||
|
||||
```
|
||||
```text
|
||||
mcp_server/prowler_mcp_server/
|
||||
├── server.py # Main orchestration
|
||||
├── prowler_hub/server.py # Hub tools (no auth)
|
||||
|
||||
@@ -83,14 +83,14 @@ npm install --save-exact mcp-remote@0.1.38
|
||||
|
||||
### 2. Local STDIO Mode
|
||||
|
||||
**Run the server locally on your machine**
|
||||
Run the server locally on your machine:
|
||||
|
||||
- Runs as a subprocess of your MCP client
|
||||
- Requires Python 3.12+ or Docker
|
||||
|
||||
### 3. Self-Hosted HTTP Mode
|
||||
|
||||
**Deploy your own remote MCP server**
|
||||
Deploy your own remote MCP server:
|
||||
|
||||
- Full control over deployment
|
||||
- Requires Python 3.12+ or Docker
|
||||
@@ -132,7 +132,7 @@ All tools follow a consistent naming pattern with prefixes:
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
```text
|
||||
prowler_mcp_server/
|
||||
├── server.py # Main orchestrator (imports sub-servers with prefixes)
|
||||
├── main.py # CLI entry point
|
||||
@@ -154,17 +154,20 @@ prowler_mcp_server/
|
||||
|
||||
The Prowler MCP Server enables powerful workflows through AI assistants:
|
||||
|
||||
**Security Operations**
|
||||
### Security Operations
|
||||
|
||||
- "Show me all critical findings from my AWS production accounts"
|
||||
- "Register my new AWS account in Prowler and run a scheduled scan every day"
|
||||
- "List all muted findings and detect what findgings are muted by a not enough good reason in relation to their severity"
|
||||
|
||||
**Security Research**
|
||||
### Security Research
|
||||
|
||||
- "Explain what the S3 bucket public access Prowler check does"
|
||||
- "Find all Prowler checks related to encryption at rest"
|
||||
- "What is the latest version of the CIS that Prowler is covering per provider?"
|
||||
|
||||
**Documentation & Learning**
|
||||
### Documentation & Learning
|
||||
|
||||
- "How do I configure Prowler to scan my GCP organization?"
|
||||
- "What authentication methods does Prowler support for Azure?"
|
||||
- "How can I contribute with a new security check to Prowler?"
|
||||
|
||||
@@ -28,12 +28,12 @@ This Terraform configuration creates the necessary IAM role and policies to allo
|
||||
|
||||
### Usage Examples
|
||||
|
||||
#### Basic deployment (without S3 integration):
|
||||
#### Basic deployment (without S3 integration)
|
||||
```bash
|
||||
terraform apply -var="external_id=your-external-id-here"
|
||||
```
|
||||
|
||||
#### With S3 integration enabled:
|
||||
#### With S3 integration enabled
|
||||
```bash
|
||||
terraform apply \
|
||||
-var="external_id=your-external-id-here" \
|
||||
@@ -42,14 +42,14 @@ terraform apply \
|
||||
-var="s3_integration_bucket_account_id=123456789012"
|
||||
```
|
||||
|
||||
#### Using terraform.tfvars file (Recommended):
|
||||
#### Using terraform.tfvars file (Recommended)
|
||||
```bash
|
||||
cp terraform.tfvars.example terraform.tfvars
|
||||
# Edit the file with your values
|
||||
terraform apply
|
||||
```
|
||||
|
||||
#### Command line variables (Alternative):
|
||||
#### Command line variables (Alternative)
|
||||
```bash
|
||||
terraform apply -var="external_id=your-external-id-here"
|
||||
```
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
> - [`prowler-compliance`](../skills/prowler-compliance/SKILL.md) - Compliance framework structure
|
||||
> - [`pytest`](../skills/pytest/SKILL.md) - Generic pytest patterns
|
||||
|
||||
### Auto-invoke Skills
|
||||
## Auto-invoke Skills
|
||||
|
||||
When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
|
||||
@@ -44,7 +44,7 @@ The Prowler SDK is the core Python engine powering cloud security assessments ac
|
||||
|
||||
### Provider Architecture
|
||||
|
||||
```
|
||||
```text
|
||||
prowler/providers/{provider}/
|
||||
├── {provider}_provider.py # Main provider class
|
||||
├── models.py # Provider-specific models
|
||||
@@ -91,7 +91,7 @@ Python 3.10+ | uv | pytest | moto (AWS mocking) | Pre-commit hooks (black, flake
|
||||
|
||||
## PROJECT STRUCTURE
|
||||
|
||||
```
|
||||
```text
|
||||
prowler/
|
||||
├── __main__.py # CLI entry point
|
||||
├── config/ # Global configuration
|
||||
|
||||
+12
-2
@@ -2,22 +2,32 @@
|
||||
|
||||
All notable changes to the **Prowler SDK** are documented in this file.
|
||||
|
||||
## [5.29.0] (Prowler UNRELEASED)
|
||||
## [5.29.0] (Prowler v5.29.0)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- `application` service for Okta provider with `application_admin_console_session_idle_timeout_15min`, `application_admin_console_mfa_required`, `application_admin_console_phishing_resistant_authentication`, `application_dashboard_mfa_required`, `application_dashboard_phishing_resistant_authentication`, and `application_authentication_policy_network_zone_enforced` checks [(#11358)](https://github.com/prowler-cloud/prowler/pull/11358)
|
||||
- AWS AI Security Framework compliance for AWS provider [(#11353)](https://github.com/prowler-cloud/prowler/pull/11353)
|
||||
- `storage_account_public_network_access_disabled` check for Azure provider and remapped the Azure CIS "Public Network Access is Disabled" requirements to it [(#11334)](https://github.com/prowler-cloud/prowler/pull/11334)
|
||||
- StackIT provider with service account key authentication [(#9237)](https://github.com/prowler-cloud/prowler/pull/9237)
|
||||
- 8 Rules service checks for Google Workspace provider using the Cloud Identity Policy API [(#11379)](https://github.com/prowler-cloud/prowler/pull/11379)
|
||||
- 12 Security service checks for Google Workspace provider using the Cloud Identity Policy API [(#11356)](https://github.com/prowler-cloud/prowler/pull/11356)
|
||||
|
||||
### ⚠️ Deprecated
|
||||
|
||||
- `s3_bucket_default_encryption` check for AWS provider since SSE-S3 is automatically applied to all S3 buckets by AWS as of January 5, 2023 and can no longer be disabled [(#11230)](https://github.com/prowler-cloud/prowler/pull/11230)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- Broken documentation URLs in Google Workspace check metadata [(#11405)](https://github.com/prowler-cloud/prowler/pull/11405)
|
||||
- ENS RD 311/2022 (AWS) compliance mapping: `vpc_different_regions` was uncorrectly mapped under the `mp.com.4` family (Network segregation). That check is now mapped to a new `op.cont.2.aws.vpc.1` requirement under the Continuity of Service control [(#11372)](https://github.com/prowler-cloud/prowler/pull/11372)
|
||||
- Compliance CSV row count now matches the UI per requirement by sourcing rows from the framework JSON's `requirement.Checks` instead of the stale `finding.compliance` snapshot [(#11370)](https://github.com/prowler-cloud/prowler/pull/11370)
|
||||
- OpenStack provider exception codes moved from the `10000-10999` range, shared with the AlibabaCloud provider, to the free `17000-17999` range to keep error codes unambiguous [(#11382)](https://github.com/prowler-cloud/prowler/pull/11382)
|
||||
- Azure provider authentication against sovereign clouds (`AzureChinaCloud`, `AzureUSGovernment`) [(#10284)](https://github.com/prowler-cloud/prowler/pull/10284)
|
||||
|
||||
---
|
||||
|
||||
## [5.28.1] (Prowler 5.28.1)
|
||||
## [5.28.1] (Prowler v5.28.1)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ from prowler.providers.okta.models import OktaOutputOptions
|
||||
from prowler.providers.openstack.models import OpenStackOutputOptions
|
||||
from prowler.providers.oraclecloud.models import OCIOutputOptions
|
||||
from prowler.providers.scaleway.models import ScalewayOutputOptions
|
||||
from prowler.providers.stackit.models import StackITOutputOptions
|
||||
from prowler.providers.vercel.models import VercelOutputOptions
|
||||
|
||||
|
||||
@@ -416,6 +417,10 @@ def prowler():
|
||||
output_options = OCIOutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
)
|
||||
elif provider == "stackit":
|
||||
output_options = StackITOutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
)
|
||||
elif provider == "alibabacloud":
|
||||
output_options = AlibabaCloudOutputOptions(
|
||||
args, bulk_checks_metadata, global_provider.identity
|
||||
|
||||
@@ -1360,7 +1360,9 @@
|
||||
{
|
||||
"Id": "4.1.1.1",
|
||||
"Description": "Ensure 2-Step Verification (Multi-Factor Authentication) is enforced for all users in administrative roles",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_2sv_enforced"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1381,7 +1383,9 @@
|
||||
{
|
||||
"Id": "4.1.1.2",
|
||||
"Description": "Ensure hardware security keys are used for all users in administrative roles and other high-value accounts",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_2sv_hardware_keys_admins"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1402,7 +1406,9 @@
|
||||
{
|
||||
"Id": "4.1.1.3",
|
||||
"Description": "Ensure 2-Step Verification (Multi-Factor Authentication) is enforced for all users",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_2sv_enforced"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1423,7 +1429,9 @@
|
||||
{
|
||||
"Id": "4.1.2.1",
|
||||
"Description": "Ensure Super Admin account recovery is disabled",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_super_admin_recovery_disabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1444,7 +1452,9 @@
|
||||
{
|
||||
"Id": "4.1.2.2",
|
||||
"Description": "Ensure User account recovery is enabled",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_user_recovery_enabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1465,7 +1475,9 @@
|
||||
{
|
||||
"Id": "4.1.3.1",
|
||||
"Description": "Ensure Advanced Protection Program is configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_advanced_protection_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1486,7 +1498,9 @@
|
||||
{
|
||||
"Id": "4.1.4.1",
|
||||
"Description": "Ensure login challenges are enforced",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_login_challenges_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1507,7 +1521,9 @@
|
||||
{
|
||||
"Id": "4.1.5.1",
|
||||
"Description": "Ensure password policy is configured for enhanced security",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_password_policy_strong"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1528,7 +1544,9 @@
|
||||
{
|
||||
"Id": "4.2.1.1",
|
||||
"Description": "Ensure application access to Google services is restricted",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_app_access_restricted"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1570,7 +1588,9 @@
|
||||
{
|
||||
"Id": "4.2.1.3",
|
||||
"Description": "Ensure internal apps can access Google Workspace APIs",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_internal_apps_trusted"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1633,7 +1653,9 @@
|
||||
{
|
||||
"Id": "4.2.3.1",
|
||||
"Description": "Ensure DLP policies for Google Drive are configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_dlp_drive_rules_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1654,7 +1676,9 @@
|
||||
{
|
||||
"Id": "4.2.4.1",
|
||||
"Description": "Ensure Google session control is configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_session_duration_limited"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1696,7 +1720,9 @@
|
||||
{
|
||||
"Id": "4.2.6.1",
|
||||
"Description": "Ensure less secure app access is disabled",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_less_secure_apps_disabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "4 Security",
|
||||
@@ -1801,7 +1827,9 @@
|
||||
{
|
||||
"Id": "6.1",
|
||||
"Description": "Ensure User's password changed is configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"rules_password_changed_alert_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6 Rules",
|
||||
@@ -1822,7 +1850,9 @@
|
||||
{
|
||||
"Id": "6.2",
|
||||
"Description": "Ensure Government-backed attacks is configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"rules_government_backed_attacks_alert_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6 Rules",
|
||||
@@ -1843,7 +1873,9 @@
|
||||
{
|
||||
"Id": "6.3",
|
||||
"Description": "Ensure User suspended due to suspicious activity is configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"rules_suspicious_activity_suspension_alert_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6 Rules",
|
||||
@@ -1864,7 +1896,9 @@
|
||||
{
|
||||
"Id": "6.4",
|
||||
"Description": "Ensure User granted Admin privilege is configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"rules_admin_privilege_granted_alert_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6 Rules",
|
||||
@@ -1885,7 +1919,9 @@
|
||||
{
|
||||
"Id": "6.5",
|
||||
"Description": "Ensure Suspicious programmatic login is configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"rules_suspicious_programmatic_login_alert_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6 Rules",
|
||||
@@ -1906,7 +1942,9 @@
|
||||
{
|
||||
"Id": "6.6",
|
||||
"Description": "Ensure Suspicious login is configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"rules_suspicious_login_alert_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6 Rules",
|
||||
@@ -1927,7 +1965,9 @@
|
||||
{
|
||||
"Id": "6.7",
|
||||
"Description": "Ensure Leaked password is configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"rules_leaked_password_alert_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6 Rules",
|
||||
@@ -1948,7 +1988,9 @@
|
||||
{
|
||||
"Id": "6.8",
|
||||
"Description": "Ensure Gmail potential employee spoofing is configured",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"rules_gmail_employee_spoofing_alert_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "6 Rules",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.1.1",
|
||||
"Description": "Phishing-resistant MFA SHALL be required for all users",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_2sv_enforced",
|
||||
"security_2sv_hardware_keys_admins"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -21,7 +24,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.1.2",
|
||||
"Description": "If phishing-resistant MFA is not yet tenable, an MFA method from the list of acceptable MFA methods SHALL be used as an interim solution",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_2sv_enforced"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -112,7 +117,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.4.1",
|
||||
"Description": "Google Workspace sessions SHALL re-authenticate after 12 hours",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_session_duration_limited"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -125,7 +132,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.5.1",
|
||||
"Description": "Password strength SHALL be enforced",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_password_policy_strong"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -138,7 +147,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.5.2",
|
||||
"Description": "Minimum password length SHALL be at least 12 characters",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_password_policy_strong"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -151,7 +162,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.5.3",
|
||||
"Description": "Minimum password length SHOULD be at least 15 characters",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_password_policy_strong"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -164,7 +177,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.5.4",
|
||||
"Description": "Password policy SHALL be enforced at next sign-in",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_password_policy_strong"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -177,7 +192,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.5.5",
|
||||
"Description": "Password reuse SHALL be restricted",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_password_policy_strong"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -244,7 +261,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.8.1",
|
||||
"Description": "Account recovery for super admins SHALL be disabled",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_super_admin_recovery_disabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -283,7 +302,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.9.1",
|
||||
"Description": "Privileged accounts SHALL be enrolled in the Advanced Protection Program",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_advanced_protection_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -296,7 +317,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.9.2",
|
||||
"Description": "Sensitive user accounts SHOULD be enrolled in the Advanced Protection Program",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_advanced_protection_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -361,7 +384,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.10.5",
|
||||
"Description": "Internal apps SHALL be allowed to access restricted Google Workspace APIs",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_internal_apps_trusted"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -402,7 +427,16 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.13.1",
|
||||
"Description": "All system-defined alerting rules SHALL be enabled with alerts sent to admin email addresses",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"rules_password_changed_alert_configured",
|
||||
"rules_government_backed_attacks_alert_configured",
|
||||
"rules_suspicious_activity_suspension_alert_configured",
|
||||
"rules_admin_privilege_granted_alert_configured",
|
||||
"rules_suspicious_programmatic_login_alert_configured",
|
||||
"rules_suspicious_login_alert_configured",
|
||||
"rules_leaked_password_alert_configured",
|
||||
"rules_gmail_employee_spoofing_alert_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
@@ -506,7 +540,9 @@
|
||||
{
|
||||
"Id": "GWS.COMMONCONTROLS.18.1",
|
||||
"Description": "A DLP policy SHALL be configured for Drive",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"security_dlp_drive_rules_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "Common Controls",
|
||||
|
||||
@@ -78,6 +78,7 @@ class Provider(str, Enum):
|
||||
SCALEWAY = "scaleway"
|
||||
VERCEL = "vercel"
|
||||
OKTA = "okta"
|
||||
STACKIT = "stackit"
|
||||
|
||||
|
||||
# Compliance
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
### Project, Check and/or Region can be * to apply for all the cases.
|
||||
### Project == <StackIT Project ID>
|
||||
### Resources and tags are lists that can have either Regex or Keywords.
|
||||
### Tags is an optional list that matches on tuples of 'key=value' and are "ANDed" together.
|
||||
### Use an alternation Regex to match one of multiple tags with "ORed" logic.
|
||||
### For each check you can except Projects, Regions, Resources and/or Tags.
|
||||
########################### MUTELIST EXAMPLE ###########################
|
||||
Mutelist:
|
||||
Accounts:
|
||||
"project_id_1":
|
||||
Checks:
|
||||
"iaas_security_group_ssh_unrestricted":
|
||||
Regions:
|
||||
- "*"
|
||||
Resources:
|
||||
- "sg-production-ssh"
|
||||
- "sg-development-rdp"
|
||||
Tags:
|
||||
- "environment=dev"
|
||||
"project_id_2":
|
||||
Checks:
|
||||
"*":
|
||||
Regions:
|
||||
- "eu01"
|
||||
Resources:
|
||||
- ".*-test$"
|
||||
@@ -1230,6 +1230,31 @@ class CheckReportNHN(Check_Report):
|
||||
self.location = getattr(resource, "location", "kr1")
|
||||
|
||||
|
||||
@dataclass
|
||||
class CheckReportStackIT(Check_Report):
|
||||
"""Contains the StackIT Check's finding information."""
|
||||
|
||||
resource_name: str
|
||||
resource_id: str
|
||||
project_id: str
|
||||
location: str
|
||||
|
||||
def __init__(self, metadata: Dict, resource: Any) -> None:
|
||||
"""Initialize the StackIT Check's finding information.
|
||||
|
||||
Args:
|
||||
metadata: The metadata of the check.
|
||||
resource: Basic information about the resource. Defaults to None.
|
||||
"""
|
||||
super().__init__(metadata, resource)
|
||||
self.resource_name = getattr(
|
||||
resource, "name", getattr(resource, "resource_name", "")
|
||||
)
|
||||
self.resource_id = getattr(resource, "id", getattr(resource, "resource_id", ""))
|
||||
self.project_id = getattr(resource, "project_id", "")
|
||||
self.location = getattr(resource, "region", getattr(resource, "location", ""))
|
||||
|
||||
|
||||
@dataclass
|
||||
class CheckReportOpenStack(Check_Report):
|
||||
"""Contains the OpenStack Check's finding information."""
|
||||
|
||||
@@ -29,10 +29,10 @@ class ProwlerArgumentParser:
|
||||
self.parser = argparse.ArgumentParser(
|
||||
prog="prowler",
|
||||
formatter_class=RawTextHelpFormatter,
|
||||
usage="prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,github,googleworkspace,okta,nhn,mongodbatlas,oraclecloud,alibabacloud,cloudflare,openstack,scaleway,vercel,dashboard,iac,image,llm} ...",
|
||||
usage="prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,github,googleworkspace,okta,nhn,mongodbatlas,oraclecloud,alibabacloud,cloudflare,openstack,scaleway,stackit,vercel,dashboard,iac,image,llm} ...",
|
||||
epilog="""
|
||||
Available Cloud Providers:
|
||||
{aws,azure,gcp,kubernetes,m365,github,googleworkspace,okta,iac,llm,image,nhn,mongodbatlas,oraclecloud,alibabacloud,cloudflare,openstack,scaleway,vercel}
|
||||
{aws,azure,gcp,kubernetes,m365,github,googleworkspace,okta,iac,llm,image,nhn,mongodbatlas,oraclecloud,alibabacloud,cloudflare,openstack,scaleway,stackit,vercel}
|
||||
aws AWS Provider
|
||||
azure Azure Provider
|
||||
gcp GCP Provider
|
||||
@@ -44,6 +44,7 @@ Available Cloud Providers:
|
||||
cloudflare Cloudflare Provider
|
||||
oraclecloud Oracle Cloud Infrastructure Provider
|
||||
openstack OpenStack Provider
|
||||
stackit StackIT Provider
|
||||
alibabacloud Alibaba Cloud Provider
|
||||
iac IaC Provider
|
||||
llm LLM Provider (Beta)
|
||||
|
||||
@@ -342,6 +342,20 @@ class Finding(BaseModel):
|
||||
output_data["resource_uid"] = check_output.resource_id
|
||||
output_data["region"] = check_output.location
|
||||
|
||||
elif provider.type == "stackit":
|
||||
output_data["auth_method"] = getattr(
|
||||
provider, "auth_method", "api_token"
|
||||
)
|
||||
output_data["account_uid"] = get_nested_attribute(
|
||||
provider, "identity.project_id"
|
||||
)
|
||||
output_data["account_name"] = get_nested_attribute(
|
||||
provider, "identity.project_name"
|
||||
)
|
||||
output_data["resource_name"] = check_output.resource_name
|
||||
output_data["resource_uid"] = check_output.resource_id
|
||||
output_data["region"] = check_output.location
|
||||
|
||||
elif provider.type == "iac":
|
||||
output_data["auth_method"] = provider.auth_method
|
||||
provider_uid = getattr(provider, "provider_uid", None)
|
||||
@@ -576,6 +590,8 @@ class Finding(BaseModel):
|
||||
finding.subscription = list(provider.identity.subscriptions.keys())[0]
|
||||
elif provider.type == "gcp":
|
||||
finding.project_id = list(provider.projects.keys())[0]
|
||||
elif provider.type == "stackit":
|
||||
finding.project_id = provider.identity.project_id
|
||||
elif provider.type == "iac":
|
||||
# For IaC, we don't have resource_line_range in the Finding model
|
||||
# It would need to be extracted from the resource metadata if needed
|
||||
|
||||
@@ -1076,6 +1076,73 @@ class HTML(Output):
|
||||
)
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def get_stackit_assessment_summary(provider: Provider) -> str:
|
||||
"""
|
||||
get_stackit_assessment_summary gets the HTML assessment summary for the StackIT provider
|
||||
|
||||
Args:
|
||||
provider (Provider): the StackIT provider object
|
||||
|
||||
Returns:
|
||||
str: HTML assessment summary for the StackIT provider
|
||||
"""
|
||||
try:
|
||||
project_id = getattr(provider.identity, "project_id", "unknown")
|
||||
project_name = getattr(provider.identity, "project_name", "")
|
||||
audited_regions = getattr(provider.identity, "audited_regions", set())
|
||||
|
||||
project_name_item = (
|
||||
f"""
|
||||
<li class="list-group-item">
|
||||
<b>Project Name:</b> {project_name}
|
||||
</li>"""
|
||||
if project_name
|
||||
else ""
|
||||
)
|
||||
|
||||
regions_item = (
|
||||
f"""
|
||||
<li class="list-group-item">
|
||||
<b>Regions:</b> {", ".join(sorted(audited_regions))}
|
||||
</li>"""
|
||||
if audited_regions
|
||||
else ""
|
||||
)
|
||||
|
||||
return f"""
|
||||
<div class="col-md-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
StackIT Assessment Summary
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<b>Project ID:</b> {project_id}
|
||||
</li>
|
||||
{project_name_item}
|
||||
{regions_item}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
StackIT Credentials
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<b>Authentication Type:</b> Service Account Key
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>"""
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}] -- {error}"
|
||||
)
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def get_cloudflare_assessment_summary(provider: Provider) -> str:
|
||||
"""
|
||||
|
||||
@@ -24,6 +24,8 @@ def stdout_report(finding, color, verbose, status, fix):
|
||||
details = finding.location
|
||||
if finding.check_metadata.Provider == "nhn":
|
||||
details = finding.location
|
||||
if finding.check_metadata.Provider == "stackit":
|
||||
details = finding.location
|
||||
if finding.check_metadata.Provider == "llm":
|
||||
details = finding.check_metadata.CheckID
|
||||
if finding.check_metadata.Provider == "iac":
|
||||
|
||||
@@ -70,6 +70,13 @@ def display_summary_table(
|
||||
elif provider.type == "nhn":
|
||||
entity_type = "Tenant Domain"
|
||||
audited_entities = provider.identity.tenant_domain
|
||||
elif provider.type == "stackit":
|
||||
if provider.identity.project_name:
|
||||
entity_type = "Project"
|
||||
audited_entities = provider.identity.project_name
|
||||
else:
|
||||
entity_type = "Project ID"
|
||||
audited_entities = provider.identity.project_id
|
||||
elif provider.type == "iac":
|
||||
if provider.scan_repository_url:
|
||||
entity_type = "Repository"
|
||||
|
||||
+4
-6
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "s3_bucket_default_encryption",
|
||||
"CheckTitle": "S3 bucket has default server-side encryption (SSE) enabled",
|
||||
"CheckTitle": "[DEPRECATED] S3 bucket has default server-side encryption (SSE) enabled",
|
||||
"CheckType": [
|
||||
"Software and Configuration Checks/AWS Security Best Practices",
|
||||
"Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices",
|
||||
@@ -14,13 +14,11 @@
|
||||
"Severity": "medium",
|
||||
"ResourceType": "AwsS3Bucket",
|
||||
"ResourceGroup": "storage",
|
||||
"Description": "**Amazon S3 buckets** have a default **server-side encryption** setting that automatically encrypts new objects using `SSE-S3` or `SSE-KMS`. This evaluates whether a bucket has a default encryption configuration defined.",
|
||||
"Description": "[DEPRECATED] **Amazon S3 buckets** have a default **server-side encryption** setting that automatically encrypts new objects using `SSE-S3` or `SSE-KMS`. This evaluates whether a bucket has a default encryption configuration defined.",
|
||||
"Risk": "Without default encryption, older objects may remain unencrypted and new uploads won't be forced to use `SSE-KMS`. This reduces confidentiality and governance by limiting key audit logs, rotation, and cross-account controls, and increases exposure if data is copied, replicated, or accessed outside intended paths.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://docs.amazonaws.cn/en_us/AmazonS3/latest/userguide/bucket-encryption.html",
|
||||
"https://aws.amazon.com/blogs/security/how-to-prevent-uploads-of-unencrypted-objects-to-amazon-s3/",
|
||||
"https://docs.aws.amazon.com/us_en/AmazonS3/latest/userguide/default-encryption-faq.html"
|
||||
"https://docs.aws.amazon.com/AmazonS3/latest/userguide/default-encryption-faq.html"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
@@ -39,5 +37,5 @@
|
||||
],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
"Notes": "This check is being deprecated since AWS automatically applies SSE-S3 to every S3 bucket (both new buckets and previously-unencrypted existing buckets) as of January 5, 2023, and encryption can no longer be disabled. For SSE-KMS validation, use `s3_bucket_kms_encryption` instead."
|
||||
}
|
||||
|
||||
@@ -241,7 +241,10 @@ class AzureProvider(Provider):
|
||||
azure_credentials = None
|
||||
if tenant_id and client_id and client_secret:
|
||||
azure_credentials = self.validate_static_credentials(
|
||||
tenant_id=tenant_id, client_id=client_id, client_secret=client_secret
|
||||
tenant_id=tenant_id,
|
||||
client_id=client_id,
|
||||
client_secret=client_secret,
|
||||
region_config=self._region_config,
|
||||
)
|
||||
|
||||
# Set up the Azure session
|
||||
@@ -410,6 +413,9 @@ class AzureProvider(Provider):
|
||||
authority=config["authority"],
|
||||
base_url=config["base_url"],
|
||||
credential_scopes=config["credential_scopes"],
|
||||
graph_host=config["graph_host"],
|
||||
graph_scope=config["graph_scope"],
|
||||
logs_endpoint=config["logs_endpoint"],
|
||||
)
|
||||
except ArgumentTypeError as validation_error:
|
||||
logger.error(
|
||||
@@ -507,6 +513,7 @@ class AzureProvider(Provider):
|
||||
tenant_id=azure_credentials["tenant_id"],
|
||||
client_id=azure_credentials["client_id"],
|
||||
client_secret=azure_credentials["client_secret"],
|
||||
authority=region_config.authority,
|
||||
)
|
||||
return credentials
|
||||
except ClientAuthenticationError as error:
|
||||
@@ -579,7 +586,10 @@ class AzureProvider(Provider):
|
||||
)
|
||||
else:
|
||||
try:
|
||||
credentials = InteractiveBrowserCredential(tenant_id=tenant_id)
|
||||
credentials = InteractiveBrowserCredential(
|
||||
tenant_id=tenant_id,
|
||||
authority=region_config.authority,
|
||||
)
|
||||
except Exception as error:
|
||||
logger.critical(
|
||||
"Failed to retrieve azure credentials using browser authentication"
|
||||
@@ -662,6 +672,7 @@ class AzureProvider(Provider):
|
||||
tenant_id=tenant_id,
|
||||
client_id=client_id,
|
||||
client_secret=client_secret,
|
||||
region_config=region_config,
|
||||
)
|
||||
|
||||
# Set up the Azure session
|
||||
@@ -675,7 +686,11 @@ class AzureProvider(Provider):
|
||||
region_config,
|
||||
)
|
||||
# Create a SubscriptionClient
|
||||
subscription_client = SubscriptionClient(credentials)
|
||||
subscription_client = SubscriptionClient(
|
||||
credentials,
|
||||
base_url=region_config.base_url,
|
||||
credential_scopes=region_config.credential_scopes,
|
||||
)
|
||||
|
||||
# Get info from the subscriptions
|
||||
available_subscriptions = []
|
||||
@@ -1039,7 +1054,11 @@ class AzureProvider(Provider):
|
||||
}
|
||||
"""
|
||||
credentials = self.session
|
||||
subscription_client = SubscriptionClient(credentials)
|
||||
subscription_client = SubscriptionClient(
|
||||
credentials,
|
||||
base_url=self.region_config.base_url,
|
||||
credential_scopes=self.region_config.credential_scopes,
|
||||
)
|
||||
locations = {}
|
||||
|
||||
for subscription_id, display_name in self._identity.subscriptions.items():
|
||||
@@ -1084,7 +1103,10 @@ class AzureProvider(Provider):
|
||||
|
||||
@staticmethod
|
||||
def validate_static_credentials(
|
||||
tenant_id: str = None, client_id: str = None, client_secret: str = None
|
||||
tenant_id: str = None,
|
||||
client_id: str = None,
|
||||
client_secret: str = None,
|
||||
region_config: AzureRegionConfig = None,
|
||||
) -> dict:
|
||||
"""
|
||||
Validates the static credentials for the Azure provider.
|
||||
@@ -1093,6 +1115,9 @@ class AzureProvider(Provider):
|
||||
tenant_id (str): The Azure Active Directory tenant ID.
|
||||
client_id (str): The Azure client ID.
|
||||
client_secret (str): The Azure client secret.
|
||||
region_config (AzureRegionConfig): The region configuration used to
|
||||
build the per-cloud login endpoint and Graph scope. Defaults to
|
||||
the public-cloud configuration when not provided.
|
||||
|
||||
Raises:
|
||||
AzureNotValidTenantIdError: If the provided Azure Tenant ID is not valid.
|
||||
@@ -1129,8 +1154,13 @@ class AzureProvider(Provider):
|
||||
message="The provided Azure Client Secret is not valid.",
|
||||
)
|
||||
|
||||
if region_config is None:
|
||||
region_config = AzureProvider.setup_region_config("AzureCloud")
|
||||
|
||||
try:
|
||||
AzureProvider.verify_client(tenant_id, client_id, client_secret)
|
||||
AzureProvider.verify_client(
|
||||
tenant_id, client_id, client_secret, region_config
|
||||
)
|
||||
return {
|
||||
"tenant_id": tenant_id,
|
||||
"client_id": client_id,
|
||||
@@ -1162,7 +1192,9 @@ class AzureProvider(Provider):
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def verify_client(tenant_id, client_id, client_secret) -> None:
|
||||
def verify_client(
|
||||
tenant_id, client_id, client_secret, region_config: AzureRegionConfig = None
|
||||
) -> None:
|
||||
"""
|
||||
Verifies the Azure client credentials using the specified tenant ID, client ID, and client secret.
|
||||
|
||||
@@ -1170,6 +1202,9 @@ class AzureProvider(Provider):
|
||||
tenant_id (str): The Azure Active Directory tenant ID.
|
||||
client_id (str): The Azure client ID.
|
||||
client_secret (str): The Azure client secret.
|
||||
region_config (AzureRegionConfig): The region configuration used to
|
||||
build the per-cloud login endpoint and Graph scope. Defaults to
|
||||
the public-cloud configuration when not provided.
|
||||
|
||||
Raises:
|
||||
AzureNotValidTenantIdError: If the provided Azure Tenant ID is not valid.
|
||||
@@ -1179,7 +1214,13 @@ class AzureProvider(Provider):
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
|
||||
if region_config is None:
|
||||
region_config = AzureProvider.setup_region_config("AzureCloud")
|
||||
# `authority` is None for the public cloud and a bare host (e.g.
|
||||
# `login.chinacloudapi.cn`) for sovereign clouds, mirroring the
|
||||
# `AzureAuthorityHosts` constants used by azure-identity.
|
||||
login_endpoint = region_config.authority or "login.microsoftonline.com"
|
||||
url = f"https://{login_endpoint}/{tenant_id}/oauth2/v2.0/token"
|
||||
headers = {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json",
|
||||
@@ -1188,7 +1229,7 @@ class AzureProvider(Provider):
|
||||
"grant_type": "client_credentials",
|
||||
"client_id": client_id,
|
||||
"client_secret": client_secret,
|
||||
"scope": "https://graph.microsoft.com/.default",
|
||||
"scope": region_config.graph_scope,
|
||||
}
|
||||
response = requests.post(url, headers=headers, data=data).json()
|
||||
if "access_token" not in response.keys() and "error_codes" in response.keys():
|
||||
|
||||
@@ -4,6 +4,18 @@ AZURE_CHINA_CLOUD = "https://management.chinacloudapi.cn"
|
||||
AZURE_US_GOV_CLOUD = "https://management.usgovcloudapi.net"
|
||||
AZURE_GENERIC_CLOUD = "https://management.azure.com"
|
||||
|
||||
AZURE_GENERIC_GRAPH_HOST = "https://graph.microsoft.com"
|
||||
AZURE_CHINA_GRAPH_HOST = "https://microsoftgraph.chinacloudapi.cn"
|
||||
AZURE_US_GOV_GRAPH_HOST = "https://graph.microsoft.us"
|
||||
|
||||
AZURE_GENERIC_GRAPH_SCOPE = f"{AZURE_GENERIC_GRAPH_HOST}/.default"
|
||||
AZURE_CHINA_GRAPH_SCOPE = f"{AZURE_CHINA_GRAPH_HOST}/.default"
|
||||
AZURE_US_GOV_GRAPH_SCOPE = f"{AZURE_US_GOV_GRAPH_HOST}/.default"
|
||||
|
||||
AZURE_GENERIC_LOGS_ENDPOINT = "https://api.loganalytics.io"
|
||||
AZURE_CHINA_LOGS_ENDPOINT = "https://api.loganalytics.azure.cn"
|
||||
AZURE_US_GOV_LOGS_ENDPOINT = "https://api.loganalytics.us"
|
||||
|
||||
|
||||
def get_regions_config(region):
|
||||
allowed_regions = {
|
||||
@@ -11,16 +23,25 @@ def get_regions_config(region):
|
||||
"authority": None,
|
||||
"base_url": AZURE_GENERIC_CLOUD,
|
||||
"credential_scopes": [AZURE_GENERIC_CLOUD + "/.default"],
|
||||
"graph_host": AZURE_GENERIC_GRAPH_HOST,
|
||||
"graph_scope": AZURE_GENERIC_GRAPH_SCOPE,
|
||||
"logs_endpoint": AZURE_GENERIC_LOGS_ENDPOINT,
|
||||
},
|
||||
"AzureChinaCloud": {
|
||||
"authority": AzureAuthorityHosts.AZURE_CHINA,
|
||||
"base_url": AZURE_CHINA_CLOUD,
|
||||
"credential_scopes": [AZURE_CHINA_CLOUD + "/.default"],
|
||||
"graph_host": AZURE_CHINA_GRAPH_HOST,
|
||||
"graph_scope": AZURE_CHINA_GRAPH_SCOPE,
|
||||
"logs_endpoint": AZURE_CHINA_LOGS_ENDPOINT,
|
||||
},
|
||||
"AzureUSGovernment": {
|
||||
"authority": AzureAuthorityHosts.AZURE_GOVERNMENT,
|
||||
"base_url": AZURE_US_GOV_CLOUD,
|
||||
"credential_scopes": [AZURE_US_GOV_CLOUD + "/.default"],
|
||||
"graph_host": AZURE_US_GOV_GRAPH_HOST,
|
||||
"graph_scope": AZURE_US_GOV_GRAPH_SCOPE,
|
||||
"logs_endpoint": AZURE_US_GOV_LOGS_ENDPOINT,
|
||||
},
|
||||
}
|
||||
return allowed_regions[region]
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
from kiota_authentication_azure.azure_identity_authentication_provider import (
|
||||
AzureIdentityAuthenticationProvider,
|
||||
)
|
||||
from msgraph.graph_request_adapter import GraphRequestAdapter
|
||||
from msgraph_core import GraphClientFactory
|
||||
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.providers.azure.azure_provider import AzureProvider
|
||||
|
||||
@@ -47,10 +53,32 @@ class AzureService:
|
||||
clients = {}
|
||||
try:
|
||||
if "GraphServiceClient" in str(service):
|
||||
clients.update({identity.tenant_domain: service(credentials=session)})
|
||||
# GraphServiceClient(credentials, scopes=...) only customises the
|
||||
# OAuth scope; the underlying httpx client's base URL stays at
|
||||
# graph.microsoft.com. For sovereign clouds we must also point
|
||||
# the HTTP transport at the per-cloud host, which is done by
|
||||
# building a custom GraphRequestAdapter with a NationalClouds
|
||||
# base URL.
|
||||
auth_provider = AzureIdentityAuthenticationProvider(
|
||||
session, scopes=[region_config.graph_scope]
|
||||
)
|
||||
http_client = GraphClientFactory.create_with_default_middleware(
|
||||
host=region_config.graph_host
|
||||
)
|
||||
request_adapter = GraphRequestAdapter(auth_provider, client=http_client)
|
||||
clients.update(
|
||||
{identity.tenant_domain: service(request_adapter=request_adapter)}
|
||||
)
|
||||
elif "LogsQueryClient" in str(service):
|
||||
for subscription_id, display_name in identity.subscriptions.items():
|
||||
clients.update({subscription_id: service(credential=session)})
|
||||
clients.update(
|
||||
{
|
||||
subscription_id: service(
|
||||
credential=session,
|
||||
endpoint=region_config.logs_endpoint,
|
||||
)
|
||||
}
|
||||
)
|
||||
else:
|
||||
for subscription_id, display_name in identity.subscriptions.items():
|
||||
clients.update(
|
||||
|
||||
@@ -20,6 +20,9 @@ class AzureRegionConfig(BaseModel):
|
||||
authority: Optional[str] = None
|
||||
base_url: str = ""
|
||||
credential_scopes: list = []
|
||||
graph_host: str = "https://graph.microsoft.com"
|
||||
graph_scope: str = "https://graph.microsoft.com/.default"
|
||||
logs_endpoint: str = "https://api.loganalytics.io"
|
||||
|
||||
|
||||
class AzureSubscription(BaseModel):
|
||||
|
||||
@@ -256,6 +256,25 @@ class Provider(ABC):
|
||||
mutelist_path=arguments.mutelist_file,
|
||||
fixer_config=fixer_config,
|
||||
)
|
||||
elif "stackit" in provider_class_name.lower():
|
||||
provider_class(
|
||||
project_id=arguments.stackit_project_id,
|
||||
service_account_key_path=getattr(
|
||||
arguments, "stackit_service_account_key_path", None
|
||||
),
|
||||
service_account_key=getattr(
|
||||
arguments, "stackit_service_account_key", None
|
||||
),
|
||||
regions=(
|
||||
set(arguments.stackit_region)
|
||||
if arguments.stackit_region
|
||||
else None
|
||||
),
|
||||
scan_unused_services=arguments.scan_unused_services,
|
||||
config_path=arguments.config_file,
|
||||
mutelist_path=arguments.mutelist_file,
|
||||
fixer_config=fixer_config,
|
||||
)
|
||||
elif "github" in provider_class_name.lower():
|
||||
orgs = []
|
||||
repos = []
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "When external Google Groups access is enabled, users can access and participate in groups created **outside the organization**, potentially exposing them to **phishing, social engineering, or data leakage** through unmanaged external group communications.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/181865",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/users/advanced/turn-on-or-off-additional-google-services",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-3
@@ -13,9 +13,8 @@
|
||||
"Risk": "Without external invitation warnings, users may unintentionally include **external guests** in internal meetings, exposing **confidential meeting details**, agendas, and internal attendee lists to unauthorized parties. This is a common vector for inadvertent data leakage through everyday calendar actions.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/6329284",
|
||||
"https://knowledge.workspace.google.com/admin/calendar/set-google-calendar-sharing-options",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/calendar/allow-external-invitations-in-google-calendar-events",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+1
-2
@@ -13,9 +13,8 @@
|
||||
"Risk": "Overly permissive external sharing of primary calendars exposes **sensitive meeting metadata** — titles, attendees, locations, and descriptions — to users outside the organization. This increases the risk of **information disclosure**, **social engineering**, and **targeted phishing** based on insights into organizational activities.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/60765",
|
||||
"https://knowledge.workspace.google.com/admin/calendar/set-google-calendar-sharing-options",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+1
-2
@@ -13,9 +13,8 @@
|
||||
"Risk": "Overly permissive external sharing of secondary calendars exposes **project-specific or team-specific event details** to users outside the organization. Because secondary calendars often hold more targeted activities (e.g., product launches, internal reviews), unrestricted external sharing increases the risk of **information disclosure** and **competitive intelligence leakage**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/60765",
|
||||
"https://knowledge.workspace.google.com/admin/calendar/set-google-calendar-sharing-options",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Unrestricted Chat app installation allows **unvetted third-party applications** to access user data including conversation content and organizational information. An attacker could distribute a malicious Chat app to **exfiltrate confidential data** or establish **persistent access** to internal communications.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/6089179",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/apps/manage-the-marketplace-app-allowlist-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Enabled external file sharing allows users to send files containing **confidential information** to external parties through Chat. This creates a **data leakage** channel that bypasses DLP controls, particularly dangerous for organizations handling **regulated data** such as PII, PHI, or financial records.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9540647",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/chat/set-up-chat-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Unrestricted external messaging allows users to communicate freely with **any external party**, increasing the risk of **data exfiltration** through conversation content and **social engineering attacks** from untrusted domains targeting internal users.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9540647",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/chat/set-up-chat-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Unrestricted external spaces allow users to add **anyone from any domain** to persistent group conversations. This increases the risk of **confidential information exposure** in shared spaces and enables **unauthorized external access** to ongoing organizational discussions.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9540647",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/chat/set-up-chat-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Exposed webhook URLs allow **unauthorized content injection** into Chat spaces. Attackers can send **fraudulent or misleading messages** that appear to come from trusted services, creating a vector for **social engineering** and **phishing** within internal communications.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/6089179",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/apps/manage-the-marketplace-app-allowlist-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Unrestricted internal file sharing in Chat allows files with **sensitive information** to be distributed freely without passing through approved channels. This undermines **data governance** and **audit trail** requirements, making it harder to track data movement within the organization.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9540647",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/chat/set-up-chat-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://knowledge.workspace.google.com/admin/users/prebuilt-administrator-roles",
|
||||
"https://support.google.com/a/answer/9011373"
|
||||
"https://knowledge.workspace.google.com/admin/users/security-best-practices-for-administrator-accounts"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://knowledge.workspace.google.com/admin/users/prebuilt-administrator-roles",
|
||||
"https://support.google.com/a/answer/9011373"
|
||||
"https://knowledge.workspace.google.com/admin/users/security-best-practices-for-administrator-accounts"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "If Access Checker suggests broader audiences or public visibility, users may **inadvertently widen access** to a file beyond the people they intended to share with. This is a common cause of unintentional internal or external over-sharing.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/60781",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/drive/manage-external-sharing-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "When Drive for desktop is enabled, organizational files are **synchronized to local devices** and remain accessible if the device is lost, stolen, or compromised. Because Drive for desktop bypasses the central offline-access controls, this channel is a frequently overlooked path for sensitive data to leave organization-managed environments.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7491144",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/drive/set-up-drive-for-desktop-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without external sharing warnings, users may unintentionally share **sensitive documents** with external recipients who are not entitled to the data. This is a common vector for inadvertent leakage of intellectual property, personally identifiable information, and confidential business data through routine Drive sharing.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/60781",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/drive/manage-external-sharing-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "If external users can move files from internal shared drives into shared drives owned by another organization, the organization **loses authoritative control** over its own data. This is a frequently overlooked path for unintentional or malicious data exfiltration through shared drive collaboration.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/60781",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/drive/manage-external-sharing-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Allowing users to publish Drive files to the web creates a path for **unbounded data exposure**. Sensitive documents, intellectual property, customer data, or internal communications can be made publicly accessible — and indexed by search engines — with a single click, often unintentionally.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/60781",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/drive/manage-external-sharing-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "When users cannot create shared drives, they store collaborative content in their personal **My Drive** instead. When that user account is deleted, the data is also deleted, leading to **unintentional data loss** of organizationally significant information. Allowing shared drive creation makes data survivable across account lifecycle events.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7212025",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://support.google.com/a/users/answer/7212025",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "When viewers and commenters can download, print, or copy shared drive files, they can **bulk-extract sensitive content** — including intellectual property, personally identifiable information, and confidential business documents — using nothing more than read access. This is one of the most direct paths to data exfiltration through Drive.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7662202",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/drive/manage-shared-drives-as-an-admin",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "If shared drive managers can override organizational defaults, **unauthorized data exposure** can occur when a manager intentionally or accidentally weakens a shared drive's security posture (for example, allowing external members or enabling download for viewers).",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7662202",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/drive/manage-shared-drives-as-an-admin",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "If non-members can be added to files inside a shared drive, the **drive's membership becomes meaningless** as a security control. Sensitive content scoped to a specific team can be silently extended to users who were never granted access to the drive itself, leading to unintended information disclosure.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7662202",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/drive/manage-shared-drives-as-an-admin",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "When external sharing is unrestricted, users can share organizational content with **any external Google account**, including untrusted or unknown parties. Restricting sharing to allowlisted domains drastically reduces the surface area for accidental and malicious data exfiltration through Drive.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/60781",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/drive/manage-external-sharing-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Allowlisted domains are still external. Users may not realize that even an allowlisted recipient is outside the organization, leading to **unintentional disclosure of sensitive content** to legitimate but external collaborators. A warning prompt at share time mitigates that without preventing the sharing itself.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/60781",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/drive/manage-external-sharing-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without protection against anomalous attachment types, users may receive **emails with unusual file formats** that are designed to bypass standard security filters. Attackers may use **uncommon file extensions or MIME types** to deliver malware that evades signature-based detection.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7676854",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/set-up-rules-to-detect-harmful-attachments",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "With auto-forwarding enabled, an attacker who gains control of a user account can create **forwarding rules to exfiltrate** all incoming email to an external address. This can persist undetected and provide the attacker with continuous access to sensitive communications even after the account is recovered.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/2491924",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/let-users-automatically-forward-their-own-gmail-emails",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without comprehensive mail storage, messages sent through other Google services (Calendar, Drive, etc.) may not be stored in Gmail and therefore **not subject to Vault retention policies**. This creates gaps in **compliance coverage**, **eDiscovery**, and **audit trails** that could violate regulatory requirements.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/3547347",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/set-up-comprehensive-mail-storage",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without protection against domain spoofing based on similar domain names, users may receive **phishing emails from lookalike domains** (e.g., examp1e.com instead of example.com) that appear legitimate. This enables **credential theft, malware delivery, and business email compromise** attacks.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9157861",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/advanced-phishing-and-malware-protection",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without protection against employee name spoofing, users may receive **emails that appear to come from colleagues or executives** but are actually from external attackers. This enables **business email compromise (BEC)**, **wire fraud**, and **social engineering attacks** that exploit trust relationships.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9157861",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/advanced-phishing-and-malware-protection",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without protection against encrypted attachments from untrusted senders, users may receive **password-protected archives containing malware** that bypass standard content scanning. Attackers commonly use encrypted attachments to evade detection and deliver **ransomware, trojans, or other malicious payloads**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7676854",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/set-up-rules-to-detect-harmful-attachments",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without enhanced pre-delivery scanning, some **sophisticated phishing and malware** messages may pass through standard filters and be delivered to users. The additional scanning layer catches threats that the first-pass filters miss, reducing the organization's exposure to **zero-day phishing campaigns** and **targeted attacks**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7380368",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/security/help-prevent-phishing-with-pre-delivery-message-scanning",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without external image scanning, attackers can use **linked images to track email opens**, deliver **exploit payloads via image rendering vulnerabilities**, or use images as part of sophisticated **phishing schemes** that mimic legitimate communications.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7676854",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/advanced-phishing-and-malware-protection",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without protection of groups from domain-spoofing emails, attackers can send **spoofed messages to group mailboxes** that appear to originate from the organization. Since groups distribute to many recipients, a single spoofed email can enable **mass phishing, social engineering, or misinformation** campaigns across the organization.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9157861",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/advanced-phishing-and-malware-protection",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without protection against inbound domain spoofing, users may receive **emails that appear to come from their own organization** but are sent by external attackers. This enables **internal impersonation**, **phishing**, and **business email compromise** attacks that exploit trust in internal communications.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9157861",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/advanced-phishing-and-malware-protection",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "If users can delegate access to their mailbox, an attacker who compromises one account could silently delegate access to maintain persistent email surveillance. This also increases the risk of **insider threats** and **data exfiltration** through shared mailbox access.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7223765",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/let-users-delegate-access-to-a-gmail-account",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "With per-user outbound gateways enabled, users can route outbound email through **external SMTP servers**, bypassing organizational **email security controls**, **DLP policies**, and **audit logging**. This creates an unmonitored channel for data exfiltration and policy circumvention.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/176652",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/allow-per-user-outbound-gateways",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "With POP and IMAP enabled, users can access email through **legacy clients** that rely on simple password authentication, bypassing **multifactor authentication** and other modern security controls. This significantly increases the risk of **credential-based account compromise**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/105694",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/sync/turn-pop-and-imap-on-or-off-for-users",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without protection against script-bearing attachments from untrusted senders, users may receive **files containing malicious scripts** that can execute harmful code when opened. Attackers commonly use script attachments to deliver **malware, backdoors, or credential stealers**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7676854",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/set-up-rules-to-detect-harmful-attachments",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without shortened URL scanning, attackers can use **URL shortening services** to hide malicious destinations in phishing emails. Users cannot visually verify where the link leads, increasing the success rate of **phishing and credential harvesting** attacks.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7676854",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/advanced-phishing-and-malware-protection",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without protection against unauthenticated emails, users may receive **spoofed or forged messages** that fail SPF and DKIM checks but are still delivered normally. This enables **phishing**, **spam**, and **impersonation attacks** that exploit the lack of sender verification.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/9157861",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/advanced-phishing-and-malware-protection",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Without untrusted link warnings, users may click on **phishing links** or links to **malware distribution sites** without any warning. This significantly increases the success rate of **social engineering attacks** targeting the organization.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/7676854",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/gmail/advanced/advanced-phishing-and-malware-protection",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Allowing any user to create groups with external members or incoming email from outside increases the risk of **unauthorized data sharing**, **spam delivery**, and **shadow IT** groups that bypass organizational controls.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/10308022",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/groups/what-you-get-with-groups-for-business",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Allowing external access to groups exposes **group names, descriptions, and membership** to anyone outside the organization, increasing the risk of **information disclosure** and enabling external parties to identify targets for **social engineering attacks**.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/10308022",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/groups/what-you-get-with-groups-for-business",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Allowing all organization users or anyone to view group conversations can lead to **information disclosure** of sensitive discussions, internal decisions, and confidential data shared within groups.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/10308022",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/groups/what-you-get-with-groups-for-business",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@
|
||||
"Risk": "Allowing unrestricted Marketplace app installation exposes the organization to **unvetted third-party applications** that may request broad OAuth scopes, potentially gaining access to **sensitive organizational data** including emails, documents, and calendar events without proper security review.",
|
||||
"RelatedUrl": "",
|
||||
"AdditionalURLs": [
|
||||
"https://support.google.com/a/answer/6089179",
|
||||
"https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
"https://knowledge.workspace.google.com/admin/apps/manage-the-marketplace-app-allowlist-for-your-organization",
|
||||
"https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings"
|
||||
],
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user