mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-06-10 13:32:44 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5e75814194 | |||
| 961b247d36 | |||
| 6abd5186aa | |||
| 627088e214 | |||
| 93ac38ca90 | |||
| aa7490aab4 | |||
| b94c8a5e5e | |||
| e6bea9f25a | |||
| 1f4e308374 | |||
| 4d569d5b79 | |||
| 5b038e631a | |||
| c5707ae9f1 | |||
| 29090adb03 | |||
| 78bd9adeed | |||
| f55983a77d | |||
| 52f98f1704 | |||
| 3afa98084f | |||
| b0ee914825 | |||
| eabe488437 | |||
| 8104382cc1 | |||
| 592c7bac81 | |||
| 3aefde14aa | |||
| 02f3e77eaf | |||
| bcd7b2d723 | |||
| 86946f3a84 | |||
| fce1e4f3d2 | |||
| 5d490fa185 | |||
| ea847d8824 | |||
| c5f7e80b20 | |||
| f5345a3982 | |||
| b539514d8d | |||
| 9acef41f96 | |||
| c40adce2ff | |||
| 378c2ff7f6 | |||
| d54095abde | |||
| a12cb5b6d6 | |||
| dde42b6a84 | |||
| 3316ec8d23 | |||
| 71220b2696 | |||
| dd730eec94 | |||
| afe2e0a09e | |||
| 507d163a50 | |||
| 530fef5106 | |||
| 5cbbceb3be | |||
| fa189e7eb9 | |||
| fb966213cc | |||
| 097a60ebc9 | |||
| db03556ef6 | |||
| ecc8eaf366 | |||
| 619d1ffc62 | |||
| 9e20cb2e5a | |||
| cb76e77851 | |||
| a24f818547 | |||
| e07687ce67 |
@@ -51,18 +51,16 @@ jobs:
|
||||
"amitsharm"
|
||||
"andoniaf"
|
||||
"cesararroba"
|
||||
"Chan9390"
|
||||
"danibarranqueroo"
|
||||
"HugoPBrito"
|
||||
"jfagoagas"
|
||||
"josemazo"
|
||||
"josema-xyz"
|
||||
"lydiavilchez"
|
||||
"mmuller88"
|
||||
"MrCloudSec"
|
||||
# "MrCloudSec"
|
||||
"pedrooot"
|
||||
"prowler-bot"
|
||||
"puchy22"
|
||||
"rakan-pro"
|
||||
"RosaRivasProwler"
|
||||
"StylusFrost"
|
||||
"toniblyx"
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
name: 'SDK: Refresh OCI Regions'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * 1' # Every Monday at 09:00 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
PYTHON_VERSION: '3.12'
|
||||
|
||||
jobs:
|
||||
refresh-oci-regions:
|
||||
if: github.repository == 'prowler-cloud/prowler'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
ref: 'master'
|
||||
|
||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install oci
|
||||
|
||||
- name: Update OCI regions
|
||||
env:
|
||||
OCI_CLI_USER: ${{ secrets.E2E_OCI_USER_ID }}
|
||||
OCI_CLI_FINGERPRINT: ${{ secrets.E2E_OCI_FINGERPRINT }}
|
||||
OCI_CLI_TENANCY: ${{ secrets.E2E_OCI_TENANCY_ID }}
|
||||
OCI_CLI_KEY_CONTENT: ${{ secrets.E2E_OCI_KEY_CONTENT }}
|
||||
OCI_CLI_REGION: ${{ secrets.E2E_OCI_REGION }}
|
||||
run: python util/update_oci_regions.py
|
||||
|
||||
- name: Create pull request
|
||||
id: create-pr
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
||||
with:
|
||||
token: ${{ secrets.PROWLER_BOT_ACCESS_TOKEN }}
|
||||
author: 'prowler-bot <179230569+prowler-bot@users.noreply.github.com>'
|
||||
committer: 'github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>'
|
||||
commit-message: 'feat(oraclecloud): update commercial regions'
|
||||
branch: 'oci-regions-update-${{ github.run_number }}'
|
||||
title: 'feat(oraclecloud): Update commercial regions'
|
||||
labels: |
|
||||
status/waiting-for-revision
|
||||
severity/low
|
||||
provider/oraclecloud
|
||||
no-changelog
|
||||
body: |
|
||||
### Description
|
||||
|
||||
Automated update of OCI commercial regions from the official Oracle Cloud Infrastructure Identity service.
|
||||
|
||||
**Trigger:** ${{ github.event_name == 'schedule' && 'Scheduled (weekly)' || github.event_name == 'workflow_dispatch' && 'Manual' || 'Workflow update' }}
|
||||
**Run:** [#${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
||||
### Changes
|
||||
|
||||
This PR updates the `OCI_COMMERCIAL_REGIONS` dictionary in `prowler/providers/oraclecloud/config.py` with the latest regions fetched from the OCI Identity API (`list_regions()`).
|
||||
|
||||
- Government regions (`OCI_GOVERNMENT_REGIONS`) are preserved unchanged
|
||||
- Region display names are mapped from Oracle's official documentation
|
||||
|
||||
### Checklist
|
||||
|
||||
- [x] This is an automated update from OCI official sources
|
||||
- [x] Government regions (us-langley-1, us-luke-1) preserved
|
||||
- [x] No manual review of region data required
|
||||
|
||||
### License
|
||||
|
||||
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
|
||||
|
||||
- name: PR creation result
|
||||
run: |
|
||||
if [[ "${{ steps.create-pr.outputs.pull-request-number }}" ]]; then
|
||||
echo "✓ Pull request #${{ steps.create-pr.outputs.pull-request-number }} created successfully"
|
||||
echo "URL: ${{ steps.create-pr.outputs.pull-request-url }}"
|
||||
else
|
||||
echo "✓ No changes detected - OCI regions are up to date"
|
||||
fi
|
||||
@@ -65,6 +65,10 @@ jobs:
|
||||
E2E_OCI_KEY_CONTENT: ${{ secrets.E2E_OCI_KEY_CONTENT }}
|
||||
E2E_OCI_REGION: ${{ secrets.E2E_OCI_REGION }}
|
||||
E2E_NEW_USER_PASSWORD: ${{ secrets.E2E_NEW_USER_PASSWORD }}
|
||||
E2E_ALIBABACLOUD_ACCOUNT_ID: ${{ secrets.E2E_ALIBABACLOUD_ACCOUNT_ID }}
|
||||
E2E_ALIBABACLOUD_ACCESS_KEY_ID: ${{ secrets.E2E_ALIBABACLOUD_ACCESS_KEY_ID }}
|
||||
E2E_ALIBABACLOUD_ACCESS_KEY_SECRET: ${{ secrets.E2E_ALIBABACLOUD_ACCESS_KEY_SECRET }}
|
||||
E2E_ALIBABACLOUD_ROLE_ARN: ${{ secrets.E2E_ALIBABACLOUD_ROLE_ARN }}
|
||||
# Pass E2E paths from impact analysis
|
||||
E2E_TEST_PATHS: ${{ needs.impact-analysis.outputs.ui-e2e }}
|
||||
RUN_ALL_TESTS: ${{ needs.impact-analysis.outputs.run-all }}
|
||||
|
||||
@@ -44,6 +44,7 @@ Use these skills for detailed patterns on-demand:
|
||||
| `prowler-commit` | Professional commits (conventional-commits) | [SKILL.md](skills/prowler-commit/SKILL.md) |
|
||||
| `prowler-pr` | Pull request conventions | [SKILL.md](skills/prowler-pr/SKILL.md) |
|
||||
| `prowler-docs` | Documentation style guide | [SKILL.md](skills/prowler-docs/SKILL.md) |
|
||||
| `prowler-attack-paths-query` | Create Attack Paths openCypher queries | [SKILL.md](skills/prowler-attack-paths-query/SKILL.md) |
|
||||
| `skill-creator` | Create new AI agent skills | [SKILL.md](skills/skill-creator/SKILL.md) |
|
||||
|
||||
### Auto-invoke Skills
|
||||
@@ -56,6 +57,7 @@ When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
| Adding DRF pagination or permissions | `django-drf` |
|
||||
| Adding new providers | `prowler-provider` |
|
||||
| Adding services to existing providers | `prowler-provider` |
|
||||
| Adding privilege escalation detection queries | `prowler-attack-paths-query` |
|
||||
| After creating/modifying a skill | `skill-sync` |
|
||||
| App Router / Server Actions | `nextjs-15` |
|
||||
| Building AI chat features | `ai-sdk-5` |
|
||||
@@ -63,6 +65,7 @@ When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
| Create PR that requires changelog entry | `prowler-changelog` |
|
||||
| Create a PR with gh pr create | `prowler-pr` |
|
||||
| Creating API endpoints | `jsonapi` |
|
||||
| Creating Attack Paths queries | `prowler-attack-paths-query` |
|
||||
| Creating ViewSets, serializers, or filters in api/ | `django-drf` |
|
||||
| Creating Zod schemas | `zod-4` |
|
||||
| Creating a git commit | `prowler-commit` |
|
||||
@@ -92,6 +95,7 @@ When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
| Understand changelog gate and no-changelog label behavior | `prowler-ci` |
|
||||
| Understand review ownership with CODEOWNERS | `prowler-pr` |
|
||||
| Update CHANGELOG.md in any component | `prowler-changelog` |
|
||||
| Updating existing Attack Paths queries | `prowler-attack-paths-query` |
|
||||
| Updating existing checks and metadata | `prowler-sdk-check` |
|
||||
| Using Zustand stores | `zustand-5` |
|
||||
| Working on MCP server tools | `prowler-mcp` |
|
||||
|
||||
@@ -104,18 +104,19 @@ Every AWS provider scan will enqueue an Attack Paths ingestion job automatically
|
||||
|
||||
| Provider | Checks | Services | [Compliance Frameworks](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/compliance/) | [Categories](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/misc/#categories) | Support | Interface |
|
||||
|---|---|---|---|---|---|---|
|
||||
| AWS | 584 | 84 | 40 | 17 | Official | UI, API, CLI |
|
||||
| Azure | 169 | 22 | 16 | 12 | Official | UI, API, CLI |
|
||||
| AWS | 585 | 84 | 40 | 17 | Official | UI, API, CLI |
|
||||
| Azure | 169 | 22 | 17 | 13 | Official | UI, API, CLI |
|
||||
| GCP | 100 | 17 | 14 | 7 | Official | UI, API, CLI |
|
||||
| Kubernetes | 84 | 7 | 7 | 9 | Official | UI, API, CLI |
|
||||
| GitHub | 20 | 2 | 1 | 2 | Official | UI, API, CLI |
|
||||
| M365 | 71 | 7 | 4 | 3 | Official | UI, API, CLI |
|
||||
| M365 | 72 | 7 | 4 | 4 | Official | UI, API, CLI |
|
||||
| OCI | 52 | 14 | 1 | 12 | Official | UI, API, CLI |
|
||||
| Alibaba Cloud | 64 | 9 | 2 | 9 | Official | UI, API, CLI |
|
||||
| Cloudflare | 23 | 2 | 0 | 5 | Official | CLI |
|
||||
| Cloudflare | 29 | 3 | 0 | 5 | Official | CLI |
|
||||
| IaC | [See `trivy` docs.](https://trivy.dev/latest/docs/coverage/iac/) | N/A | N/A | N/A | Official | UI, API, CLI |
|
||||
| MongoDB Atlas | 10 | 3 | 0 | 3 | Official | UI, API, CLI |
|
||||
| LLM | [See `promptfoo` docs.](https://www.promptfoo.dev/docs/red-team/plugins/) | N/A | N/A | N/A | Official | CLI |
|
||||
| OpenStack | 1 | 1 | 0 | 2 | Official | CLI |
|
||||
| NHN | 6 | 2 | 1 | 0 | Unofficial | CLI |
|
||||
|
||||
> [!Note]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
> **Skills Reference**: For detailed patterns, use these skills:
|
||||
> - [`prowler-api`](../skills/prowler-api/SKILL.md) - Models, Serializers, Views, RLS patterns
|
||||
> - [`prowler-test-api`](../skills/prowler-test-api/SKILL.md) - Testing patterns (pytest-django)
|
||||
> - [`prowler-attack-paths-query`](../skills/prowler-attack-paths-query/SKILL.md) - Attack Paths openCypher queries
|
||||
> - [`django-drf`](../skills/django-drf/SKILL.md) - Generic DRF patterns
|
||||
> - [`jsonapi`](../skills/jsonapi/SKILL.md) - Strict JSON:API v1.1 spec compliance
|
||||
> - [`pytest`](../skills/pytest/SKILL.md) - Generic pytest patterns
|
||||
@@ -15,9 +16,11 @@ When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
|--------|-------|
|
||||
| Add changelog entry for a PR or feature | `prowler-changelog` |
|
||||
| Adding DRF pagination or permissions | `django-drf` |
|
||||
| Adding privilege escalation detection queries | `prowler-attack-paths-query` |
|
||||
| Committing changes | `prowler-commit` |
|
||||
| Create PR that requires changelog entry | `prowler-changelog` |
|
||||
| Creating API endpoints | `jsonapi` |
|
||||
| Creating Attack Paths queries | `prowler-attack-paths-query` |
|
||||
| Creating ViewSets, serializers, or filters in api/ | `django-drf` |
|
||||
| Creating a git commit | `prowler-commit` |
|
||||
| Creating/modifying models, views, serializers | `prowler-api` |
|
||||
@@ -27,6 +30,7 @@ When performing these actions, ALWAYS invoke the corresponding skill FIRST:
|
||||
| Reviewing JSON:API compliance | `jsonapi` |
|
||||
| Testing RLS tenant isolation | `prowler-test-api` |
|
||||
| Update CHANGELOG.md in any component | `prowler-changelog` |
|
||||
| Updating existing Attack Paths queries | `prowler-attack-paths-query` |
|
||||
| Writing Prowler API tests | `prowler-test-api` |
|
||||
| Writing Python tests with pytest | `pytest` |
|
||||
|
||||
|
||||
@@ -2,6 +2,37 @@
|
||||
|
||||
All notable changes to the **Prowler API** are documented in this file.
|
||||
|
||||
## [1.20.0] (Prowler UNRELEASED)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- OpenStack provider support [(#10003)](https://github.com/prowler-cloud/prowler/pull/10003)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
- Attack Paths: Queries definition now has short description and attribution [(#9983)](https://github.com/prowler-cloud/prowler/pull/9983)
|
||||
- Attack Paths: Internet node is created while scan [(#9992)](https://github.com/prowler-cloud/prowler/pull/9992)
|
||||
- Attack Paths: Add full paths set from [pathfinding.cloud](https://pathfinding.cloud/) [(#10008)](https://github.com/prowler-cloud/prowler/pull/10008)
|
||||
- Support CSA CCM 4.0 for the AWS provider [(#10018)](https://github.com/prowler-cloud/prowler/pull/10018)
|
||||
- Support CSA CCM 4.0 for the GCP provider [(#10042)](https://github.com/prowler-cloud/prowler/pull/10042)
|
||||
- Support CSA CCM 4.0 for the Azure provider [(#10039)](https://github.com/prowler-cloud/prowler/pull/10039)
|
||||
- Support CSA CCM 4.0 for the Oracle Cloud provider [(#10057)](https://github.com/prowler-cloud/prowler/pull/10057)
|
||||
- Support CSA CCM 4.0 for the Alibaba Cloud provider [(#10061)](https://github.com/prowler-cloud/prowler/pull/10061)
|
||||
|
||||
### 🔐 Security
|
||||
|
||||
- Pillow 12.1.1 (CVE-2021-25289) [(#10027)](https://github.com/prowler-cloud/prowler/pull/10027)
|
||||
|
||||
---
|
||||
|
||||
## [1.19.2] (Prowler v5.18.2)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- SAML role mapping now prevents removing the last MANAGE_ACCOUNT user [(#10007)](https://github.com/prowler-cloud/prowler/pull/10007)
|
||||
|
||||
---
|
||||
|
||||
## [1.19.0] (Prowler v5.18.0)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
Generated
+105
-460
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -24,7 +24,7 @@ dependencies = [
|
||||
"drf-spectacular-jsonapi==0.5.1",
|
||||
"gunicorn==23.0.0",
|
||||
"lxml==5.3.2",
|
||||
"prowler @ git+https://github.com/prowler-cloud/prowler.git@v5.18",
|
||||
"prowler @ git+https://github.com/prowler-cloud/prowler.git@master",
|
||||
"psycopg2-binary==2.9.9",
|
||||
"pytest-celery[redis] (>=1.0.1,<2.0.0)",
|
||||
"sentry-sdk[django] (>=2.20.0,<3.0.0)",
|
||||
@@ -49,7 +49,7 @@ name = "prowler-api"
|
||||
package-mode = false
|
||||
# Needed for the SDK compatibility
|
||||
requires-python = ">=3.11,<3.13"
|
||||
version = "1.19.1"
|
||||
version = "1.20.0"
|
||||
|
||||
[project.scripts]
|
||||
celery = "src.backend.config.settings.celery"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,14 @@
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class AttackPathsQueryAttribution:
|
||||
"""Source attribution for an Attack Path query."""
|
||||
|
||||
text: str
|
||||
link: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class AttackPathsQueryParameterDefinition:
|
||||
"""
|
||||
@@ -23,7 +31,9 @@ class AttackPathsQueryDefinition:
|
||||
|
||||
id: str
|
||||
name: str
|
||||
short_description: str
|
||||
description: str
|
||||
provider: str
|
||||
cypher: str
|
||||
attribution: AttackPathsQueryAttribution | None = None
|
||||
parameters: list[AttackPathsQueryParameterDefinition] = field(default_factory=list)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Generated by Django migration for OpenStack provider support
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
import api.db_utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("api", "0075_cloudflare_provider"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="provider",
|
||||
name="provider",
|
||||
field=api.db_utils.ProviderEnumField(
|
||||
choices=[
|
||||
("aws", "AWS"),
|
||||
("azure", "Azure"),
|
||||
("gcp", "GCP"),
|
||||
("kubernetes", "Kubernetes"),
|
||||
("m365", "M365"),
|
||||
("github", "GitHub"),
|
||||
("mongodbatlas", "MongoDB Atlas"),
|
||||
("iac", "IaC"),
|
||||
("oraclecloud", "Oracle Cloud Infrastructure"),
|
||||
("alibabacloud", "Alibaba Cloud"),
|
||||
("cloudflare", "Cloudflare"),
|
||||
("openstack", "OpenStack"),
|
||||
],
|
||||
default="aws",
|
||||
),
|
||||
),
|
||||
migrations.RunSQL(
|
||||
"ALTER TYPE provider ADD VALUE IF NOT EXISTS 'openstack';",
|
||||
reverse_sql=migrations.RunSQL.noop,
|
||||
),
|
||||
]
|
||||
@@ -288,6 +288,7 @@ class Provider(RowLevelSecurityProtectedModel):
|
||||
ORACLECLOUD = "oraclecloud", _("Oracle Cloud Infrastructure")
|
||||
ALIBABACLOUD = "alibabacloud", _("Alibaba Cloud")
|
||||
CLOUDFLARE = "cloudflare", _("Cloudflare")
|
||||
OPENSTACK = "openstack", _("OpenStack")
|
||||
|
||||
@staticmethod
|
||||
def validate_aws_uid(value):
|
||||
@@ -410,6 +411,15 @@ class Provider(RowLevelSecurityProtectedModel):
|
||||
pointer="/data/attributes/uid",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def validate_openstack_uid(value):
|
||||
if not re.match(r"^[a-zA-Z0-9][a-zA-Z0-9._-]{0,254}$", value):
|
||||
raise ModelValidationError(
|
||||
detail="OpenStack provider ID must be a valid project ID (UUID or project name).",
|
||||
code="openstack-uid",
|
||||
pointer="/data/attributes/uid",
|
||||
)
|
||||
|
||||
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
||||
inserted_at = models.DateTimeField(auto_now_add=True, editable=False)
|
||||
updated_at = models.DateTimeField(auto_now=True, editable=False)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Prowler API
|
||||
version: 1.19.1
|
||||
version: 1.20.0
|
||||
description: |-
|
||||
Prowler API specification.
|
||||
|
||||
@@ -367,6 +367,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -380,6 +381,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -398,6 +400,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -413,6 +416,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -616,7 +620,7 @@ paths:
|
||||
operationId: attack_paths_scans_queries_retrieve
|
||||
description: Retrieve the catalog of Attack Paths queries available for this
|
||||
Attack Paths scan.
|
||||
summary: List attack paths queries
|
||||
summary: List Attack Paths queries
|
||||
parameters:
|
||||
- in: query
|
||||
name: fields[attack-paths-scans]
|
||||
@@ -714,7 +718,7 @@ paths:
|
||||
description: Bad request (e.g., Unknown Attack Paths query for the selected
|
||||
provider)
|
||||
'404':
|
||||
description: No attack paths found for the given query and parameters
|
||||
description: No Attack Paths found for the given query and parameters
|
||||
'500':
|
||||
description: Attack Paths query execution failed due to a database error
|
||||
/api/v1/compliance-overviews:
|
||||
@@ -1348,6 +1352,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -1361,6 +1366,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -1379,6 +1385,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -1394,6 +1401,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -1938,6 +1946,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -1951,6 +1960,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -1969,6 +1979,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -1984,6 +1995,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -2436,6 +2448,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -2449,6 +2462,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -2467,6 +2481,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -2482,6 +2497,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -2932,6 +2948,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -2945,6 +2962,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -2963,6 +2981,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -2978,6 +2997,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -3416,6 +3436,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -3429,6 +3450,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -3447,6 +3469,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -3462,6 +3485,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -5241,6 +5265,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -5254,6 +5279,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -5272,6 +5298,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -5287,6 +5314,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- name: filter[search]
|
||||
@@ -5404,6 +5432,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -5417,6 +5446,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -5435,6 +5465,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -5450,6 +5481,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- name: filter[search]
|
||||
@@ -5556,6 +5588,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -5569,6 +5602,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -5586,6 +5620,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -5601,6 +5636,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- name: filter[search]
|
||||
@@ -5739,6 +5775,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -5752,6 +5789,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -5770,6 +5808,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -5785,6 +5824,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -5936,6 +5976,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -5949,6 +5990,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -5967,6 +6009,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -5982,6 +6025,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -6127,6 +6171,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -6140,6 +6185,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -6157,6 +6203,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -6172,6 +6219,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- name: filter[search]
|
||||
@@ -6359,6 +6407,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -6372,6 +6421,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -6390,6 +6440,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -6405,6 +6456,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -6521,6 +6573,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -6534,6 +6587,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -6552,6 +6606,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -6567,6 +6622,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -6707,6 +6763,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -6720,6 +6777,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -6738,6 +6796,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -6753,6 +6812,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -7534,6 +7594,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -7547,6 +7608,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider__in]
|
||||
schema:
|
||||
@@ -7565,6 +7627,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -7580,6 +7643,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -7598,6 +7662,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -7611,6 +7676,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -7629,6 +7695,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -7644,6 +7711,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- name: filter[search]
|
||||
@@ -8285,6 +8353,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -8298,6 +8367,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -8316,6 +8386,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -8331,6 +8402,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -8787,6 +8859,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -8800,6 +8873,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -8818,6 +8892,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -8833,6 +8908,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -9102,6 +9178,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -9115,6 +9192,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -9133,6 +9211,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -9148,6 +9227,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -9423,6 +9503,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -9436,6 +9517,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -9454,6 +9536,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -9469,6 +9552,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -10278,6 +10362,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -10291,6 +10376,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -10309,6 +10395,7 @@ paths:
|
||||
- kubernetes
|
||||
- m365
|
||||
- mongodbatlas
|
||||
- openstack
|
||||
- oraclecloud
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
@@ -10324,6 +10411,7 @@ paths:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -12438,6 +12526,8 @@ components:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
short_description:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
provider:
|
||||
@@ -12446,12 +12536,42 @@ components:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/AttackPathsQueryParameter'
|
||||
attribution:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/AttackPathsQueryAttribution'
|
||||
nullable: true
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
- short_description
|
||||
- description
|
||||
- provider
|
||||
- parameters
|
||||
AttackPathsQueryAttribution:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- id
|
||||
additionalProperties: false
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description: The [type](https://jsonapi.org/format/#document-resource-object-identification)
|
||||
member is used to describe resource objects that share common attributes
|
||||
and relationships.
|
||||
enum:
|
||||
- attack-paths-query-attributions
|
||||
id: {}
|
||||
attributes:
|
||||
type: object
|
||||
properties:
|
||||
text:
|
||||
type: string
|
||||
link:
|
||||
type: string
|
||||
required:
|
||||
- text
|
||||
- link
|
||||
AttackPathsQueryParameter:
|
||||
type: object
|
||||
required:
|
||||
@@ -17316,6 +17436,50 @@ components:
|
||||
required:
|
||||
- api_key
|
||||
- api_email
|
||||
- type: object
|
||||
title: OpenStack clouds.yaml Credentials
|
||||
properties:
|
||||
clouds_yaml_content:
|
||||
type: string
|
||||
description: The full content of a clouds.yaml configuration
|
||||
file.
|
||||
clouds_yaml_cloud:
|
||||
type: string
|
||||
description: The name of the cloud to use from the clouds.yaml
|
||||
file.
|
||||
required:
|
||||
- clouds_yaml_content
|
||||
- clouds_yaml_cloud
|
||||
- type: object
|
||||
title: OpenStack Explicit Credentials
|
||||
properties:
|
||||
auth_url:
|
||||
type: string
|
||||
description: OpenStack Keystone authentication URL (e.g.,
|
||||
https://openstack.example.com:5000/v3).
|
||||
username:
|
||||
type: string
|
||||
description: OpenStack username for authentication.
|
||||
password:
|
||||
type: string
|
||||
description: OpenStack password for authentication.
|
||||
region_name:
|
||||
type: string
|
||||
description: OpenStack region name (e.g., RegionOne).
|
||||
identity_api_version:
|
||||
type: string
|
||||
description: Keystone API version (default: 3).
|
||||
user_domain_name:
|
||||
type: string
|
||||
description: User domain name (default: Default).
|
||||
project_domain_name:
|
||||
type: string
|
||||
description: Project domain name (default: Default).
|
||||
required:
|
||||
- auth_url
|
||||
- username
|
||||
- password
|
||||
- region_name
|
||||
writeOnly: true
|
||||
required:
|
||||
- secret
|
||||
@@ -18315,6 +18479,7 @@ components:
|
||||
- oraclecloud
|
||||
- alibabacloud
|
||||
- cloudflare
|
||||
- openstack
|
||||
type: string
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -18328,6 +18493,7 @@ components:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
x-spec-enum-id: 2d8d323e9cc0044b
|
||||
uid:
|
||||
type: string
|
||||
@@ -18445,6 +18611,7 @@ components:
|
||||
- oraclecloud
|
||||
- alibabacloud
|
||||
- cloudflare
|
||||
- openstack
|
||||
type: string
|
||||
x-spec-enum-id: 2d8d323e9cc0044b
|
||||
description: |-
|
||||
@@ -18461,6 +18628,7 @@ components:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
uid:
|
||||
type: string
|
||||
title: Unique identifier for the provider, set by the provider
|
||||
@@ -18509,6 +18677,7 @@ components:
|
||||
- oraclecloud
|
||||
- alibabacloud
|
||||
- cloudflare
|
||||
- openstack
|
||||
type: string
|
||||
x-spec-enum-id: 2d8d323e9cc0044b
|
||||
description: |-
|
||||
@@ -18525,6 +18694,7 @@ components:
|
||||
* `oraclecloud` - Oracle Cloud Infrastructure
|
||||
* `alibabacloud` - Alibaba Cloud
|
||||
* `cloudflare` - Cloudflare
|
||||
* `openstack` - OpenStack
|
||||
uid:
|
||||
type: string
|
||||
minLength: 3
|
||||
@@ -19346,6 +19516,48 @@ components:
|
||||
required:
|
||||
- api_key
|
||||
- api_email
|
||||
- type: object
|
||||
title: OpenStack clouds.yaml Credentials
|
||||
properties:
|
||||
clouds_yaml_content:
|
||||
type: string
|
||||
description: The full content of a clouds.yaml configuration file.
|
||||
clouds_yaml_cloud:
|
||||
type: string
|
||||
description: The name of the cloud to use from the clouds.yaml
|
||||
file.
|
||||
required:
|
||||
- clouds_yaml_content
|
||||
- clouds_yaml_cloud
|
||||
- type: object
|
||||
title: OpenStack Explicit Credentials
|
||||
properties:
|
||||
auth_url:
|
||||
type: string
|
||||
description: OpenStack Keystone authentication URL (e.g., https://openstack.example.com:5000/v3).
|
||||
username:
|
||||
type: string
|
||||
description: OpenStack username for authentication.
|
||||
password:
|
||||
type: string
|
||||
description: OpenStack password for authentication.
|
||||
region_name:
|
||||
type: string
|
||||
description: OpenStack region name (e.g., RegionOne).
|
||||
identity_api_version:
|
||||
type: string
|
||||
description: Keystone API version (default: 3).
|
||||
user_domain_name:
|
||||
type: string
|
||||
description: User domain name (default: Default).
|
||||
project_domain_name:
|
||||
type: string
|
||||
description: Project domain name (default: Default).
|
||||
required:
|
||||
- auth_url
|
||||
- username
|
||||
- password
|
||||
- region_name
|
||||
writeOnly: true
|
||||
required:
|
||||
- secret_type
|
||||
@@ -19732,6 +19944,50 @@ components:
|
||||
required:
|
||||
- api_key
|
||||
- api_email
|
||||
- type: object
|
||||
title: OpenStack clouds.yaml Credentials
|
||||
properties:
|
||||
clouds_yaml_content:
|
||||
type: string
|
||||
description: The full content of a clouds.yaml configuration
|
||||
file.
|
||||
clouds_yaml_cloud:
|
||||
type: string
|
||||
description: The name of the cloud to use from the clouds.yaml
|
||||
file.
|
||||
required:
|
||||
- clouds_yaml_content
|
||||
- clouds_yaml_cloud
|
||||
- type: object
|
||||
title: OpenStack Explicit Credentials
|
||||
properties:
|
||||
auth_url:
|
||||
type: string
|
||||
description: OpenStack Keystone authentication URL (e.g.,
|
||||
https://openstack.example.com:5000/v3).
|
||||
username:
|
||||
type: string
|
||||
description: OpenStack username for authentication.
|
||||
password:
|
||||
type: string
|
||||
description: OpenStack password for authentication.
|
||||
region_name:
|
||||
type: string
|
||||
description: OpenStack region name (e.g., RegionOne).
|
||||
identity_api_version:
|
||||
type: string
|
||||
description: Keystone API version (default: 3).
|
||||
user_domain_name:
|
||||
type: string
|
||||
description: User domain name (default: Default).
|
||||
project_domain_name:
|
||||
type: string
|
||||
description: Project domain name (default: Default).
|
||||
required:
|
||||
- auth_url
|
||||
- username
|
||||
- password
|
||||
- region_name
|
||||
writeOnly: true
|
||||
required:
|
||||
- secret_type
|
||||
@@ -20130,6 +20386,48 @@ components:
|
||||
required:
|
||||
- api_key
|
||||
- api_email
|
||||
- type: object
|
||||
title: OpenStack clouds.yaml Credentials
|
||||
properties:
|
||||
clouds_yaml_content:
|
||||
type: string
|
||||
description: The full content of a clouds.yaml configuration file.
|
||||
clouds_yaml_cloud:
|
||||
type: string
|
||||
description: The name of the cloud to use from the clouds.yaml
|
||||
file.
|
||||
required:
|
||||
- clouds_yaml_content
|
||||
- clouds_yaml_cloud
|
||||
- type: object
|
||||
title: OpenStack Explicit Credentials
|
||||
properties:
|
||||
auth_url:
|
||||
type: string
|
||||
description: OpenStack Keystone authentication URL (e.g., https://openstack.example.com:5000/v3).
|
||||
username:
|
||||
type: string
|
||||
description: OpenStack username for authentication.
|
||||
password:
|
||||
type: string
|
||||
description: OpenStack password for authentication.
|
||||
region_name:
|
||||
type: string
|
||||
description: OpenStack region name (e.g., RegionOne).
|
||||
identity_api_version:
|
||||
type: string
|
||||
description: Keystone API version (default: 3).
|
||||
user_domain_name:
|
||||
type: string
|
||||
description: User domain name (default: Default).
|
||||
project_domain_name:
|
||||
type: string
|
||||
description: Project domain name (default: Default).
|
||||
required:
|
||||
- auth_url
|
||||
- username
|
||||
- password
|
||||
- region_name
|
||||
writeOnly: true
|
||||
required:
|
||||
- secret
|
||||
|
||||
@@ -83,6 +83,7 @@ def test_execute_attack_paths_query_serializes_graph(
|
||||
definition = attack_paths_query_definition_factory(
|
||||
id="aws-rds",
|
||||
name="RDS",
|
||||
short_description="Short desc",
|
||||
description="",
|
||||
cypher="MATCH (n) RETURN n",
|
||||
parameters=[],
|
||||
@@ -143,6 +144,7 @@ def test_execute_attack_paths_query_wraps_graph_errors(
|
||||
definition = attack_paths_query_definition_factory(
|
||||
id="aws-rds",
|
||||
name="RDS",
|
||||
short_description="Short desc",
|
||||
description="",
|
||||
cypher="MATCH (n) RETURN n",
|
||||
parameters=[],
|
||||
|
||||
@@ -27,6 +27,7 @@ from prowler.providers.iac.iac_provider import IacProvider
|
||||
from prowler.providers.kubernetes.kubernetes_provider import KubernetesProvider
|
||||
from prowler.providers.m365.m365_provider import M365Provider
|
||||
from prowler.providers.mongodbatlas.mongodbatlas_provider import MongodbatlasProvider
|
||||
from prowler.providers.openstack.openstack_provider import OpenstackProvider
|
||||
from prowler.providers.oraclecloud.oraclecloud_provider import OraclecloudProvider
|
||||
|
||||
|
||||
@@ -120,6 +121,7 @@ class TestReturnProwlerProvider:
|
||||
(Provider.ProviderChoices.IAC.value, IacProvider),
|
||||
(Provider.ProviderChoices.ALIBABACLOUD.value, AlibabacloudProvider),
|
||||
(Provider.ProviderChoices.CLOUDFLARE.value, CloudflareProvider),
|
||||
(Provider.ProviderChoices.OPENSTACK.value, OpenstackProvider),
|
||||
],
|
||||
)
|
||||
def test_return_prowler_provider(self, provider_type, expected_provider):
|
||||
@@ -227,6 +229,10 @@ class TestGetProwlerProviderKwargs:
|
||||
Provider.ProviderChoices.CLOUDFLARE.value,
|
||||
{"filter_accounts": ["provider_uid"]},
|
||||
),
|
||||
(
|
||||
Provider.ProviderChoices.OPENSTACK.value,
|
||||
{},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_get_prowler_provider_kwargs(self, provider_type, expected_extra_kwargs):
|
||||
|
||||
@@ -1179,6 +1179,11 @@ class TestProviderViewSet:
|
||||
"uid": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
|
||||
"alias": "Cloudflare Account",
|
||||
},
|
||||
{
|
||||
"provider": "openstack",
|
||||
"uid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
"alias": "OpenStack Project",
|
||||
},
|
||||
]
|
||||
),
|
||||
)
|
||||
@@ -1598,6 +1603,26 @@ class TestProviderViewSet:
|
||||
"cloudflare-uid",
|
||||
"uid",
|
||||
),
|
||||
# OpenStack UID validation - starts with special character
|
||||
(
|
||||
{
|
||||
"provider": "openstack",
|
||||
"uid": "-invalid-project",
|
||||
"alias": "test",
|
||||
},
|
||||
"openstack-uid",
|
||||
"uid",
|
||||
),
|
||||
# OpenStack UID validation - too short (below min_length)
|
||||
(
|
||||
{
|
||||
"provider": "openstack",
|
||||
"uid": "ab",
|
||||
"alias": "test",
|
||||
},
|
||||
"min_length",
|
||||
"uid",
|
||||
),
|
||||
]
|
||||
),
|
||||
)
|
||||
@@ -1771,21 +1796,21 @@ class TestProviderViewSet:
|
||||
(
|
||||
"uid.icontains",
|
||||
"1",
|
||||
9,
|
||||
10,
|
||||
),
|
||||
("alias", "aws_testing_1", 1),
|
||||
("alias.icontains", "aws", 2),
|
||||
("inserted_at", TODAY, 10),
|
||||
("inserted_at", TODAY, 11),
|
||||
(
|
||||
"inserted_at.gte",
|
||||
"2024-01-01",
|
||||
10,
|
||||
11,
|
||||
),
|
||||
("inserted_at.lte", "2024-01-01", 0),
|
||||
(
|
||||
"updated_at.gte",
|
||||
"2024-01-01",
|
||||
10,
|
||||
11,
|
||||
),
|
||||
("updated_at.lte", "2024-01-01", 0),
|
||||
]
|
||||
@@ -2392,6 +2417,15 @@ class TestProviderSecretViewSet:
|
||||
"api_email": "user@example.com",
|
||||
},
|
||||
),
|
||||
# OpenStack with clouds.yaml content
|
||||
(
|
||||
Provider.ProviderChoices.OPENSTACK.value,
|
||||
ProviderSecret.TypeChoices.STATIC,
|
||||
{
|
||||
"clouds_yaml_content": "clouds:\n mycloud:\n auth:\n auth_url: https://openstack.example.com:5000/v3\n",
|
||||
"clouds_yaml_cloud": "mycloud",
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_provider_secrets_create_valid(
|
||||
@@ -3830,6 +3864,7 @@ class TestAttackPathsScanViewSet:
|
||||
AttackPathsQueryDefinition(
|
||||
id="aws-rds",
|
||||
name="RDS inventory",
|
||||
short_description="List account RDS assets.",
|
||||
description="List account RDS assets",
|
||||
provider=provider.provider,
|
||||
cypher="MATCH (n) RETURN n",
|
||||
@@ -3892,6 +3927,7 @@ class TestAttackPathsScanViewSet:
|
||||
query_definition = AttackPathsQueryDefinition(
|
||||
id="aws-rds",
|
||||
name="RDS inventory",
|
||||
short_description="List account RDS assets.",
|
||||
description="List account RDS assets",
|
||||
provider=provider.provider,
|
||||
cypher="MATCH (n) RETURN n",
|
||||
@@ -4049,6 +4085,7 @@ class TestAttackPathsScanViewSet:
|
||||
query_definition = AttackPathsQueryDefinition(
|
||||
id="aws-empty",
|
||||
name="empty",
|
||||
short_description="",
|
||||
description="",
|
||||
provider=provider.provider,
|
||||
cypher="MATCH (n) RETURN n",
|
||||
@@ -10841,25 +10878,20 @@ class TestTenantFinishACSView:
|
||||
assert "sso_saml_failed=true" in response.url
|
||||
|
||||
def test_dispatch_skips_role_mapping_when_single_manage_account_user(
|
||||
self, create_test_user, tenants_fixture, saml_setup, settings, monkeypatch
|
||||
self,
|
||||
create_test_user,
|
||||
tenants_fixture,
|
||||
admin_role_fixture,
|
||||
saml_setup,
|
||||
settings,
|
||||
monkeypatch,
|
||||
):
|
||||
"""Test that role mapping is skipped when tenant has only one user with MANAGE_ACCOUNT role"""
|
||||
monkeypatch.setenv("SAML_SSO_CALLBACK_URL", "http://localhost/sso-complete")
|
||||
user = create_test_user
|
||||
tenant = tenants_fixture[0]
|
||||
|
||||
# Create a single role with manage_account=True for the user
|
||||
admin_role = Role.objects.using(MainRouter.admin_db).create(
|
||||
name="admin",
|
||||
tenant=tenant,
|
||||
manage_account=True,
|
||||
manage_users=True,
|
||||
manage_billing=True,
|
||||
manage_providers=True,
|
||||
manage_integrations=True,
|
||||
manage_scans=True,
|
||||
unlimited_visibility=True,
|
||||
)
|
||||
admin_role = admin_role_fixture
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db).create(
|
||||
user=user, role=admin_role, tenant_id=tenant.id
|
||||
)
|
||||
@@ -10930,35 +10962,26 @@ class TestTenantFinishACSView:
|
||||
.exists()
|
||||
)
|
||||
|
||||
def test_dispatch_applies_role_mapping_when_multiple_manage_account_users(
|
||||
self, create_test_user, tenants_fixture, saml_setup, settings, monkeypatch
|
||||
def test_dispatch_skips_role_mapping_when_last_manage_account_user_maps_to_existing_role(
|
||||
self,
|
||||
create_test_user,
|
||||
tenants_fixture,
|
||||
admin_role_fixture,
|
||||
roles_fixture,
|
||||
saml_setup,
|
||||
settings,
|
||||
monkeypatch,
|
||||
):
|
||||
"""Test that role mapping is applied when tenant has multiple users with MANAGE_ACCOUNT role"""
|
||||
"""Test that role mapping is skipped when it would remove the last MANAGE_ACCOUNT user"""
|
||||
monkeypatch.setenv("SAML_SSO_CALLBACK_URL", "http://localhost/sso-complete")
|
||||
user = create_test_user
|
||||
tenant = tenants_fixture[0]
|
||||
|
||||
# Create a second user with manage_account=True
|
||||
second_admin = User.objects.using(MainRouter.admin_db).create(
|
||||
email="admin2@prowler.com", name="Second Admin"
|
||||
)
|
||||
admin_role = Role.objects.using(MainRouter.admin_db).create(
|
||||
name="admin",
|
||||
tenant=tenant,
|
||||
manage_account=True,
|
||||
manage_users=True,
|
||||
manage_billing=True,
|
||||
manage_providers=True,
|
||||
manage_integrations=True,
|
||||
manage_scans=True,
|
||||
unlimited_visibility=True,
|
||||
)
|
||||
admin_role = admin_role_fixture
|
||||
viewer_role = roles_fixture[3]
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db).create(
|
||||
user=user, role=admin_role, tenant_id=tenant.id
|
||||
)
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db).create(
|
||||
user=second_admin, role=admin_role, tenant_id=tenant.id
|
||||
)
|
||||
|
||||
social_account = SocialAccount(
|
||||
user=user,
|
||||
@@ -10967,7 +10990,7 @@ class TestTenantFinishACSView:
|
||||
"firstName": ["John"],
|
||||
"lastName": ["Doe"],
|
||||
"organization": ["testing_company"],
|
||||
"userType": ["viewer"], # This SHOULD be applied
|
||||
"userType": [viewer_role.name],
|
||||
},
|
||||
)
|
||||
|
||||
@@ -11005,10 +11028,91 @@ class TestTenantFinishACSView:
|
||||
|
||||
assert response.status_code == 302
|
||||
|
||||
# Verify the viewer role was created and assigned (role mapping was applied)
|
||||
viewer_role = Role.objects.using(MainRouter.admin_db).get(
|
||||
name="viewer", tenant=tenant
|
||||
assert (
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db)
|
||||
.filter(user=user, role=admin_role, tenant_id=tenant.id)
|
||||
.exists()
|
||||
)
|
||||
assert not (
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db)
|
||||
.filter(user=user, role=viewer_role, tenant_id=tenant.id)
|
||||
.exists()
|
||||
)
|
||||
|
||||
def test_dispatch_applies_role_mapping_when_multiple_manage_account_users(
|
||||
self,
|
||||
create_test_user,
|
||||
tenants_fixture,
|
||||
admin_role_fixture,
|
||||
roles_fixture,
|
||||
saml_setup,
|
||||
settings,
|
||||
monkeypatch,
|
||||
):
|
||||
"""Test that role mapping is applied when tenant has multiple users with MANAGE_ACCOUNT role"""
|
||||
monkeypatch.setenv("SAML_SSO_CALLBACK_URL", "http://localhost/sso-complete")
|
||||
user = create_test_user
|
||||
tenant = tenants_fixture[0]
|
||||
|
||||
# Create a second user with manage_account=True
|
||||
second_admin = User.objects.using(MainRouter.admin_db).create(
|
||||
email="admin2@prowler.com", name="Second Admin"
|
||||
)
|
||||
admin_role = admin_role_fixture
|
||||
viewer_role = roles_fixture[3]
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db).create(
|
||||
user=user, role=admin_role, tenant_id=tenant.id
|
||||
)
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db).create(
|
||||
user=second_admin, role=admin_role, tenant_id=tenant.id
|
||||
)
|
||||
|
||||
social_account = SocialAccount(
|
||||
user=user,
|
||||
provider="saml",
|
||||
extra_data={
|
||||
"firstName": ["John"],
|
||||
"lastName": ["Doe"],
|
||||
"organization": ["testing_company"],
|
||||
"userType": [viewer_role.name], # This SHOULD be applied
|
||||
},
|
||||
)
|
||||
|
||||
request = RequestFactory().get(
|
||||
reverse("saml_finish_acs", kwargs={"organization_slug": "testtenant"})
|
||||
)
|
||||
request.user = user
|
||||
request.session = {}
|
||||
|
||||
with (
|
||||
patch(
|
||||
"allauth.socialaccount.providers.saml.views.get_app_or_404"
|
||||
) as mock_get_app_or_404,
|
||||
patch(
|
||||
"allauth.socialaccount.models.SocialApp.objects.get"
|
||||
) as mock_socialapp_get,
|
||||
patch(
|
||||
"allauth.socialaccount.models.SocialAccount.objects.get"
|
||||
) as mock_sa_get,
|
||||
patch("api.models.SAMLDomainIndex.objects.get") as mock_saml_domain_get,
|
||||
patch("api.models.SAMLConfiguration.objects.get") as mock_saml_config_get,
|
||||
patch("api.models.User.objects.get") as mock_user_get,
|
||||
):
|
||||
mock_get_app_or_404.return_value = MagicMock(
|
||||
provider="saml", client_id="testtenant", name="Test App", settings={}
|
||||
)
|
||||
mock_sa_get.return_value = social_account
|
||||
mock_socialapp_get.return_value = MagicMock(provider_id="saml")
|
||||
mock_saml_domain_get.return_value = SimpleNamespace(tenant_id=tenant.id)
|
||||
mock_saml_config_get.return_value = MagicMock()
|
||||
mock_user_get.return_value = user
|
||||
|
||||
view = TenantFinishACSView.as_view()
|
||||
response = view(request, organization_slug="testtenant")
|
||||
|
||||
assert response.status_code == 302
|
||||
|
||||
# Verify the viewer role was assigned (role mapping was applied)
|
||||
assert (
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db)
|
||||
.filter(user=user, role=viewer_role, tenant_id=tenant.id)
|
||||
@@ -11022,6 +11126,86 @@ class TestTenantFinishACSView:
|
||||
.exists()
|
||||
)
|
||||
|
||||
def test_dispatch_applies_role_mapping_for_non_admin_user_with_single_admin(
|
||||
self,
|
||||
create_test_user,
|
||||
tenants_fixture,
|
||||
admin_role_fixture,
|
||||
roles_fixture,
|
||||
saml_setup,
|
||||
settings,
|
||||
monkeypatch,
|
||||
):
|
||||
"""Test that role mapping is applied for a non-admin user when a single admin exists"""
|
||||
monkeypatch.setenv("SAML_SSO_CALLBACK_URL", "http://localhost/sso-complete")
|
||||
admin_user = create_test_user
|
||||
tenant = tenants_fixture[0]
|
||||
non_admin_user = User.objects.using(MainRouter.admin_db).create(
|
||||
email="viewer@prowler.com", name="Viewer"
|
||||
)
|
||||
|
||||
admin_role = admin_role_fixture
|
||||
viewer_role = roles_fixture[3]
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db).create(
|
||||
user=admin_user, role=admin_role, tenant_id=tenant.id
|
||||
)
|
||||
|
||||
social_account = SocialAccount(
|
||||
user=non_admin_user,
|
||||
provider="saml",
|
||||
extra_data={
|
||||
"firstName": ["Jane"],
|
||||
"lastName": ["Doe"],
|
||||
"organization": ["testing_company"],
|
||||
"userType": [viewer_role.name],
|
||||
},
|
||||
)
|
||||
|
||||
request = RequestFactory().get(
|
||||
reverse("saml_finish_acs", kwargs={"organization_slug": "testtenant"})
|
||||
)
|
||||
request.user = non_admin_user
|
||||
request.session = {}
|
||||
|
||||
with (
|
||||
patch(
|
||||
"allauth.socialaccount.providers.saml.views.get_app_or_404"
|
||||
) as mock_get_app_or_404,
|
||||
patch(
|
||||
"allauth.socialaccount.models.SocialApp.objects.get"
|
||||
) as mock_socialapp_get,
|
||||
patch(
|
||||
"allauth.socialaccount.models.SocialAccount.objects.get"
|
||||
) as mock_sa_get,
|
||||
patch("api.models.SAMLDomainIndex.objects.get") as mock_saml_domain_get,
|
||||
patch("api.models.SAMLConfiguration.objects.get") as mock_saml_config_get,
|
||||
patch("api.models.User.objects.get") as mock_user_get,
|
||||
):
|
||||
mock_get_app_or_404.return_value = MagicMock(
|
||||
provider="saml", client_id="testtenant", name="Test App", settings={}
|
||||
)
|
||||
mock_sa_get.return_value = social_account
|
||||
mock_socialapp_get.return_value = MagicMock(provider_id="saml")
|
||||
mock_saml_domain_get.return_value = SimpleNamespace(tenant_id=tenant.id)
|
||||
mock_saml_config_get.return_value = MagicMock()
|
||||
mock_user_get.return_value = non_admin_user
|
||||
|
||||
view = TenantFinishACSView.as_view()
|
||||
response = view(request, organization_slug="testtenant")
|
||||
|
||||
assert response.status_code == 302
|
||||
|
||||
assert (
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db)
|
||||
.filter(user=non_admin_user, role=viewer_role, tenant_id=tenant.id)
|
||||
.exists()
|
||||
)
|
||||
assert (
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db)
|
||||
.filter(user=admin_user, role=admin_role, tenant_id=tenant.id)
|
||||
.exists()
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestLighthouseConfigViewSet:
|
||||
|
||||
@@ -33,6 +33,7 @@ if TYPE_CHECKING:
|
||||
from prowler.providers.mongodbatlas.mongodbatlas_provider import (
|
||||
MongodbatlasProvider,
|
||||
)
|
||||
from prowler.providers.openstack.openstack_provider import OpenstackProvider
|
||||
from prowler.providers.oraclecloud.oraclecloud_provider import OraclecloudProvider
|
||||
|
||||
|
||||
@@ -78,12 +79,14 @@ def return_prowler_provider(
|
||||
AlibabacloudProvider
|
||||
| AwsProvider
|
||||
| AzureProvider
|
||||
| CloudflareProvider
|
||||
| GcpProvider
|
||||
| GithubProvider
|
||||
| IacProvider
|
||||
| KubernetesProvider
|
||||
| M365Provider
|
||||
| MongodbatlasProvider
|
||||
| OpenstackProvider
|
||||
| OraclecloudProvider
|
||||
):
|
||||
"""Return the Prowler provider class based on the given provider type.
|
||||
@@ -92,7 +95,7 @@ def return_prowler_provider(
|
||||
provider (Provider): The provider object containing the provider type and associated secrets.
|
||||
|
||||
Returns:
|
||||
AlibabacloudProvider | AwsProvider | AzureProvider | CloudflareProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | MongodbatlasProvider | OraclecloudProvider: The corresponding provider class.
|
||||
AlibabacloudProvider | AwsProvider | AzureProvider | CloudflareProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | MongodbatlasProvider | OpenstackProvider | OraclecloudProvider: The corresponding provider class.
|
||||
|
||||
Raises:
|
||||
ValueError: If the provider type specified in `provider.provider` is not supported.
|
||||
@@ -152,6 +155,10 @@ def return_prowler_provider(
|
||||
)
|
||||
|
||||
prowler_provider = CloudflareProvider
|
||||
case Provider.ProviderChoices.OPENSTACK.value:
|
||||
from prowler.providers.openstack.openstack_provider import OpenstackProvider
|
||||
|
||||
prowler_provider = OpenstackProvider
|
||||
case _:
|
||||
raise ValueError(f"Provider type {provider.provider} not supported")
|
||||
return prowler_provider
|
||||
@@ -208,6 +215,12 @@ def get_prowler_provider_kwargs(
|
||||
**prowler_provider_kwargs,
|
||||
"filter_accounts": [provider.uid],
|
||||
}
|
||||
elif provider.provider == Provider.ProviderChoices.OPENSTACK.value:
|
||||
# No extra kwargs needed: clouds_yaml_content and clouds_yaml_cloud from the
|
||||
# secret are sufficient. Validating project_id (provider.uid) against the
|
||||
# clouds.yaml is not feasible because not all auth methods include it and the
|
||||
# Keystone API is unavailable on public clouds.
|
||||
pass
|
||||
|
||||
if mutelist_processor:
|
||||
mutelist_content = mutelist_processor.configuration.get("Mutelist", {})
|
||||
@@ -232,6 +245,7 @@ def initialize_prowler_provider(
|
||||
| KubernetesProvider
|
||||
| M365Provider
|
||||
| MongodbatlasProvider
|
||||
| OpenstackProvider
|
||||
| OraclecloudProvider
|
||||
):
|
||||
"""Initialize a Prowler provider instance based on the given provider type.
|
||||
@@ -241,7 +255,7 @@ def initialize_prowler_provider(
|
||||
mutelist_processor (Processor): The mutelist processor object containing the mutelist configuration.
|
||||
|
||||
Returns:
|
||||
AlibabacloudProvider | AwsProvider | AzureProvider | CloudflareProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | MongodbatlasProvider | OraclecloudProvider: An instance of the corresponding provider class
|
||||
AlibabacloudProvider | AwsProvider | AzureProvider | CloudflareProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | MongodbatlasProvider | OpenstackProvider | OraclecloudProvider: An instance of the corresponding provider class
|
||||
initialized with the provider's secrets.
|
||||
"""
|
||||
prowler_provider = return_prowler_provider(provider)
|
||||
@@ -276,6 +290,13 @@ def prowler_provider_connection_test(provider: Provider) -> Connection:
|
||||
if "access_token" in prowler_provider_kwargs:
|
||||
iac_test_kwargs["access_token"] = prowler_provider_kwargs["access_token"]
|
||||
return prowler_provider.test_connection(**iac_test_kwargs)
|
||||
elif provider.provider == Provider.ProviderChoices.OPENSTACK.value:
|
||||
openstack_kwargs = {
|
||||
"clouds_yaml_content": prowler_provider_kwargs["clouds_yaml_content"],
|
||||
"clouds_yaml_cloud": prowler_provider_kwargs["clouds_yaml_cloud"],
|
||||
"raise_on_exception": False,
|
||||
}
|
||||
return prowler_provider.test_connection(**openstack_kwargs)
|
||||
else:
|
||||
return prowler_provider.test_connection(
|
||||
**prowler_provider_kwargs,
|
||||
|
||||
@@ -373,6 +373,21 @@ from rest_framework_json_api import serializers
|
||||
},
|
||||
"required": ["api_key", "api_email"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "OpenStack clouds.yaml Credentials",
|
||||
"properties": {
|
||||
"clouds_yaml_content": {
|
||||
"type": "string",
|
||||
"description": "The full content of a clouds.yaml configuration file.",
|
||||
},
|
||||
"clouds_yaml_cloud": {
|
||||
"type": "string",
|
||||
"description": "The name of the cloud to use from the clouds.yaml file.",
|
||||
},
|
||||
},
|
||||
"required": ["clouds_yaml_content", "clouds_yaml_cloud"],
|
||||
},
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1176,6 +1176,14 @@ class AttackPathsScanSerializer(RLSSerializer):
|
||||
return provider.uid if provider else None
|
||||
|
||||
|
||||
class AttackPathsQueryAttributionSerializer(BaseSerializerV1):
|
||||
text = serializers.CharField()
|
||||
link = serializers.CharField()
|
||||
|
||||
class JSONAPIMeta:
|
||||
resource_name = "attack-paths-query-attributions"
|
||||
|
||||
|
||||
class AttackPathsQueryParameterSerializer(BaseSerializerV1):
|
||||
name = serializers.CharField()
|
||||
label = serializers.CharField()
|
||||
@@ -1190,7 +1198,9 @@ class AttackPathsQueryParameterSerializer(BaseSerializerV1):
|
||||
class AttackPathsQuerySerializer(BaseSerializerV1):
|
||||
id = serializers.CharField()
|
||||
name = serializers.CharField()
|
||||
short_description = serializers.CharField()
|
||||
description = serializers.CharField()
|
||||
attribution = AttackPathsQueryAttributionSerializer(allow_null=True, required=False)
|
||||
provider = serializers.CharField()
|
||||
parameters = AttackPathsQueryParameterSerializer(many=True)
|
||||
|
||||
@@ -1515,6 +1525,8 @@ class BaseWriteProviderSecretSerializer(BaseWriteSerializer):
|
||||
"or both 'api_key' and 'api_email'."
|
||||
}
|
||||
)
|
||||
elif provider_type == Provider.ProviderChoices.OPENSTACK.value:
|
||||
serializer = OpenStackCloudsYamlProviderSecret(data=secret)
|
||||
else:
|
||||
raise serializers.ValidationError(
|
||||
{"provider": f"Provider type not supported {provider_type}"}
|
||||
@@ -1681,6 +1693,14 @@ class CloudflareApiKeyProviderSecret(serializers.Serializer):
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
class OpenStackCloudsYamlProviderSecret(serializers.Serializer):
|
||||
clouds_yaml_content = serializers.CharField()
|
||||
clouds_yaml_cloud = serializers.CharField()
|
||||
|
||||
class Meta:
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
class AlibabaCloudProviderSecret(serializers.Serializer):
|
||||
access_key_id = serializers.CharField()
|
||||
access_key_secret = serializers.CharField()
|
||||
|
||||
@@ -392,7 +392,7 @@ class SchemaView(SpectacularAPIView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
spectacular_settings.TITLE = "Prowler API"
|
||||
spectacular_settings.VERSION = "1.19.1"
|
||||
spectacular_settings.VERSION = "1.20.0"
|
||||
spectacular_settings.DESCRIPTION = (
|
||||
"Prowler API specification.\n\nThis file is auto-generated."
|
||||
)
|
||||
@@ -763,27 +763,40 @@ class TenantFinishACSView(FinishACSView):
|
||||
.tenant
|
||||
)
|
||||
|
||||
# Check if tenant has only one user with MANAGE_ACCOUNT role
|
||||
users_with_manage_account = (
|
||||
role_name = (
|
||||
extra.get("userType", ["no_permissions"])[0].strip()
|
||||
if extra.get("userType")
|
||||
else "no_permissions"
|
||||
)
|
||||
role = (
|
||||
Role.objects.using(MainRouter.admin_db)
|
||||
.filter(name=role_name, tenant=tenant)
|
||||
.first()
|
||||
)
|
||||
|
||||
# Only skip mapping if it would remove the last MANAGE_ACCOUNT user
|
||||
remaining_manage_account_users = (
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db)
|
||||
.filter(role__manage_account=True, tenant_id=tenant.id)
|
||||
.exclude(user_id=user_id)
|
||||
.values("user")
|
||||
.distinct()
|
||||
.count()
|
||||
)
|
||||
user_has_manage_account = (
|
||||
UserRoleRelationship.objects.using(MainRouter.admin_db)
|
||||
.filter(role__manage_account=True, tenant_id=tenant.id, user_id=user_id)
|
||||
.exists()
|
||||
)
|
||||
role_manage_account = role.manage_account if role else False
|
||||
would_remove_last_manage_account = (
|
||||
user_has_manage_account
|
||||
and remaining_manage_account_users == 0
|
||||
and not role_manage_account
|
||||
)
|
||||
|
||||
# Only apply role mapping from userType if tenant does NOT have exactly one user with MANAGE_ACCOUNT
|
||||
if users_with_manage_account != 1:
|
||||
role_name = (
|
||||
extra.get("userType", ["no_permissions"])[0].strip()
|
||||
if extra.get("userType")
|
||||
else "no_permissions"
|
||||
)
|
||||
try:
|
||||
role = Role.objects.using(MainRouter.admin_db).get(
|
||||
name=role_name, tenant=tenant
|
||||
)
|
||||
except Role.DoesNotExist:
|
||||
if not would_remove_last_manage_account:
|
||||
if role is None:
|
||||
role = Role.objects.using(MainRouter.admin_db).create(
|
||||
name=role_name,
|
||||
tenant=tenant,
|
||||
|
||||
@@ -18,6 +18,10 @@ DATABASES = {
|
||||
|
||||
DATABASE_ROUTERS = []
|
||||
TESTING = True
|
||||
# Override page size for testing to a value only slightly above the current fixture count.
|
||||
# We explicitly set PAGE_SIZE to 15 (round number just above fixture) to avoid masking pagination bugs, while not setting it excessively high.
|
||||
# If you add more providers to the fixture, please review that the total value is below the current one and update this value if needed.
|
||||
REST_FRAMEWORK["PAGE_SIZE"] = 15 # noqa: F405
|
||||
SECRETS_ENCRYPTION_KEY = "ZMiYVo7m4Fbe2eXXPyrwxdJss2WSalXSv3xHBcJkPl0="
|
||||
|
||||
# DRF Simple API Key settings
|
||||
|
||||
@@ -537,6 +537,12 @@ def providers_fixture(tenants_fixture):
|
||||
alias="cloudflare_testing",
|
||||
tenant_id=tenant.id,
|
||||
)
|
||||
provider11 = Provider.objects.create(
|
||||
provider="openstack",
|
||||
uid="a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||||
alias="openstack_testing",
|
||||
tenant_id=tenant.id,
|
||||
)
|
||||
|
||||
return (
|
||||
provider1,
|
||||
@@ -549,6 +555,7 @@ def providers_fixture(tenants_fixture):
|
||||
provider8,
|
||||
provider9,
|
||||
provider10,
|
||||
provider11,
|
||||
)
|
||||
|
||||
|
||||
@@ -1663,6 +1670,7 @@ def attack_paths_query_definition_factory():
|
||||
definition_payload = {
|
||||
"id": "aws-test",
|
||||
"name": "Attack Paths Test Query",
|
||||
"short_description": "Synthetic short description for tests.",
|
||||
"description": "Synthetic Attack Paths definition for tests.",
|
||||
"provider": "aws",
|
||||
"cypher": "RETURN 1",
|
||||
|
||||
@@ -12,8 +12,10 @@ BATCH_SIZE = env.int("ATTACK_PATHS_BATCH_SIZE", 1000)
|
||||
# Neo4j internal labels (Prowler-specific, not provider-specific)
|
||||
# - `ProwlerFinding`: Label for finding nodes created by Prowler and linked to cloud resources.
|
||||
# - `ProviderResource`: Added to ALL synced nodes for provider isolation and drop/query ops.
|
||||
# - `Internet`: Singleton node representing external internet access for exposed-resource queries.
|
||||
PROWLER_FINDING_LABEL = "ProwlerFinding"
|
||||
PROVIDER_RESOURCE_LABEL = "ProviderResource"
|
||||
INTERNET_NODE_LABEL = "Internet"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
@@ -6,6 +6,7 @@ from cartography.client.core.tx import run_write_query
|
||||
from celery.utils.log import get_task_logger
|
||||
|
||||
from tasks.jobs.attack_paths.config import (
|
||||
INTERNET_NODE_LABEL,
|
||||
PROWLER_FINDING_LABEL,
|
||||
PROVIDER_RESOURCE_LABEL,
|
||||
)
|
||||
@@ -30,6 +31,8 @@ FINDINGS_INDEX_STATEMENTS = [
|
||||
f"CREATE INDEX prowler_finding_provider_uid IF NOT EXISTS FOR (n:{PROWLER_FINDING_LABEL}) ON (n.provider_uid);",
|
||||
f"CREATE INDEX prowler_finding_lastupdated IF NOT EXISTS FOR (n:{PROWLER_FINDING_LABEL}) ON (n.lastupdated);",
|
||||
f"CREATE INDEX prowler_finding_status IF NOT EXISTS FOR (n:{PROWLER_FINDING_LABEL}) ON (n.status);",
|
||||
# Internet node index for MERGE lookups
|
||||
f"CREATE INDEX internet_id IF NOT EXISTS FOR (n:{INTERNET_NODE_LABEL}) ON (n.id);",
|
||||
]
|
||||
|
||||
# Indexes for provider resource sync operations
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
Internet node enrichment for Attack Paths graph.
|
||||
|
||||
Creates a real Internet node and CAN_ACCESS relationships to
|
||||
internet-exposed resources (EC2Instance, LoadBalancer, LoadBalancerV2)
|
||||
in the temporary scan database before sync.
|
||||
"""
|
||||
|
||||
import neo4j
|
||||
|
||||
from cartography.config import Config as CartographyConfig
|
||||
from celery.utils.log import get_task_logger
|
||||
|
||||
from api.models import Provider
|
||||
from prowler.config import config as ProwlerConfig
|
||||
from tasks.jobs.attack_paths.config import get_root_node_label
|
||||
from tasks.jobs.attack_paths.queries import (
|
||||
CREATE_CAN_ACCESS_RELATIONSHIPS_TEMPLATE,
|
||||
CREATE_INTERNET_NODE,
|
||||
render_cypher_template,
|
||||
)
|
||||
|
||||
logger = get_task_logger(__name__)
|
||||
|
||||
|
||||
def analysis(
|
||||
neo4j_session: neo4j.Session,
|
||||
prowler_api_provider: Provider,
|
||||
config: CartographyConfig,
|
||||
) -> int:
|
||||
"""
|
||||
Create Internet node and CAN_ACCESS relationships to exposed resources.
|
||||
|
||||
Args:
|
||||
neo4j_session: Active Neo4j session (temp database).
|
||||
prowler_api_provider: The Prowler API provider instance.
|
||||
config: Cartography configuration with update_tag.
|
||||
|
||||
Returns:
|
||||
Number of CAN_ACCESS relationships created.
|
||||
"""
|
||||
provider_uid = str(prowler_api_provider.uid)
|
||||
|
||||
parameters = {
|
||||
"provider_uid": provider_uid,
|
||||
"last_updated": config.update_tag,
|
||||
"prowler_version": ProwlerConfig.prowler_version,
|
||||
}
|
||||
|
||||
logger.info(f"Creating Internet node for provider {provider_uid}")
|
||||
neo4j_session.run(CREATE_INTERNET_NODE, parameters)
|
||||
|
||||
query = render_cypher_template(
|
||||
CREATE_CAN_ACCESS_RELATIONSHIPS_TEMPLATE,
|
||||
{"__ROOT_LABEL__": get_root_node_label(prowler_api_provider.provider)},
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Creating CAN_ACCESS relationships from Internet to exposed resources for {provider_uid}"
|
||||
)
|
||||
result = neo4j_session.run(query, parameters)
|
||||
relationships_merged = result.single().get("relationships_merged", 0)
|
||||
|
||||
logger.info(
|
||||
f"Created {relationships_merged} CAN_ACCESS relationships for provider {provider_uid}"
|
||||
)
|
||||
return relationships_merged
|
||||
@@ -1,5 +1,6 @@
|
||||
# Cypher query templates for Attack Paths operations
|
||||
from tasks.jobs.attack_paths.config import (
|
||||
INTERNET_NODE_LABEL,
|
||||
PROWLER_FINDING_LABEL,
|
||||
PROVIDER_RESOURCE_LABEL,
|
||||
)
|
||||
@@ -91,6 +92,37 @@ CLEANUP_FINDINGS_TEMPLATE = f"""
|
||||
RETURN COUNT(finding) AS deleted_findings_count
|
||||
"""
|
||||
|
||||
# Internet queries (used by internet.py)
|
||||
# ---------------------------------------
|
||||
|
||||
CREATE_INTERNET_NODE = f"""
|
||||
MERGE (internet:{INTERNET_NODE_LABEL} {{id: 'Internet'}})
|
||||
ON CREATE SET
|
||||
internet.name = 'Internet',
|
||||
internet.firstseen = timestamp(),
|
||||
internet.lastupdated = $last_updated,
|
||||
internet._module_name = 'cartography:prowler',
|
||||
internet._module_version = $prowler_version
|
||||
ON MATCH SET
|
||||
internet.lastupdated = $last_updated
|
||||
"""
|
||||
|
||||
CREATE_CAN_ACCESS_RELATIONSHIPS_TEMPLATE = f"""
|
||||
MATCH (account:__ROOT_LABEL__ {{id: $provider_uid}})-->(resource)
|
||||
WHERE resource.exposed_internet = true
|
||||
WITH resource
|
||||
MATCH (internet:{INTERNET_NODE_LABEL} {{id: 'Internet'}})
|
||||
MERGE (internet)-[r:CAN_ACCESS]->(resource)
|
||||
ON CREATE SET
|
||||
r.firstseen = timestamp(),
|
||||
r.lastupdated = $last_updated,
|
||||
r._module_name = 'cartography:prowler',
|
||||
r._module_version = $prowler_version
|
||||
ON MATCH SET
|
||||
r.lastupdated = $last_updated
|
||||
RETURN COUNT(r) AS relationships_merged
|
||||
"""
|
||||
|
||||
# Sync queries (used by sync.py)
|
||||
# -------------------------------
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ from api.models import (
|
||||
StateChoices,
|
||||
)
|
||||
from api.utils import initialize_prowler_provider
|
||||
from tasks.jobs.attack_paths import db_utils, findings, sync, utils
|
||||
from tasks.jobs.attack_paths import db_utils, findings, internet, sync, utils
|
||||
from tasks.jobs.attack_paths.config import get_cartography_ingestion_function
|
||||
|
||||
# Without this Celery goes crazy with Cartography logging
|
||||
@@ -135,7 +135,15 @@ def run(tenant_id: str, scan_id: str, task_id: str) -> dict[str, Any]:
|
||||
cartography_analysis.run(tmp_neo4j_session, tmp_cartography_config)
|
||||
db_utils.update_attack_paths_scan_progress(attack_paths_scan, 96)
|
||||
|
||||
# Adding Prowler nodes and relationships
|
||||
# Creating Internet node and CAN_ACCESS relationships
|
||||
logger.info(
|
||||
f"Creating Internet graph for AWS account {prowler_api_provider.uid}"
|
||||
)
|
||||
internet.analysis(
|
||||
tmp_neo4j_session, prowler_api_provider, tmp_cartography_config
|
||||
)
|
||||
|
||||
# Adding Prowler Finding nodes and relationships
|
||||
logger.info(
|
||||
f"Syncing Prowler analysis for AWS account {prowler_api_provider.uid}"
|
||||
)
|
||||
|
||||
@@ -35,6 +35,11 @@ from prowler.lib.outputs.compliance.cis.cis_github import GithubCIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_kubernetes import KubernetesCIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_m365 import M365CIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_oraclecloud import OracleCloudCIS
|
||||
from prowler.lib.outputs.compliance.csa.csa_alibabacloud import AlibabaCloudCSA
|
||||
from prowler.lib.outputs.compliance.csa.csa_aws import AWSCSA
|
||||
from prowler.lib.outputs.compliance.csa.csa_azure import AzureCSA
|
||||
from prowler.lib.outputs.compliance.csa.csa_gcp import GCPCSA
|
||||
from prowler.lib.outputs.compliance.csa.csa_oraclecloud import OracleCloudCSA
|
||||
from prowler.lib.outputs.compliance.ens.ens_aws import AWSENS
|
||||
from prowler.lib.outputs.compliance.ens.ens_azure import AzureENS
|
||||
from prowler.lib.outputs.compliance.ens.ens_gcp import GCPENS
|
||||
@@ -90,6 +95,7 @@ COMPLIANCE_CLASS_MAP = {
|
||||
(lambda name: name == "prowler_threatscore_aws", ProwlerThreatScoreAWS),
|
||||
(lambda name: name == "ccc_aws", CCC_AWS),
|
||||
(lambda name: name.startswith("c5_"), AWSC5),
|
||||
(lambda name: name.startswith("csa_"), AWSCSA),
|
||||
],
|
||||
"azure": [
|
||||
(lambda name: name.startswith("cis_"), AzureCIS),
|
||||
@@ -99,6 +105,7 @@ COMPLIANCE_CLASS_MAP = {
|
||||
(lambda name: name == "ccc_azure", CCC_Azure),
|
||||
(lambda name: name == "prowler_threatscore_azure", ProwlerThreatScoreAzure),
|
||||
(lambda name: name == "c5_azure", AzureC5),
|
||||
(lambda name: name.startswith("csa_"), AzureCSA),
|
||||
],
|
||||
"gcp": [
|
||||
(lambda name: name.startswith("cis_"), GCPCIS),
|
||||
@@ -108,6 +115,7 @@ COMPLIANCE_CLASS_MAP = {
|
||||
(lambda name: name == "prowler_threatscore_gcp", ProwlerThreatScoreGCP),
|
||||
(lambda name: name == "ccc_gcp", CCC_GCP),
|
||||
(lambda name: name == "c5_gcp", GCPC5),
|
||||
(lambda name: name.startswith("csa_"), GCPCSA),
|
||||
],
|
||||
"kubernetes": [
|
||||
(lambda name: name.startswith("cis_"), KubernetesCIS),
|
||||
@@ -131,9 +139,11 @@ COMPLIANCE_CLASS_MAP = {
|
||||
],
|
||||
"oraclecloud": [
|
||||
(lambda name: name.startswith("cis_"), OracleCloudCIS),
|
||||
(lambda name: name.startswith("csa_"), OracleCloudCSA),
|
||||
],
|
||||
"alibabacloud": [
|
||||
(lambda name: name.startswith("cis_"), AlibabaCloudCIS),
|
||||
(lambda name: name.startswith("csa_"), AlibabaCloudCSA),
|
||||
(
|
||||
lambda name: name == "prowler_threatscore_alibabacloud",
|
||||
ProwlerThreatScoreAlibaba,
|
||||
|
||||
@@ -4,6 +4,7 @@ from unittest.mock import MagicMock, call, patch
|
||||
|
||||
import pytest
|
||||
from tasks.jobs.attack_paths import findings as findings_module
|
||||
from tasks.jobs.attack_paths import internet as internet_module
|
||||
from tasks.jobs.attack_paths.scan import run as attack_paths_run
|
||||
|
||||
from api.models import (
|
||||
@@ -37,6 +38,7 @@ class TestAttackPathsRun:
|
||||
@patch("tasks.jobs.attack_paths.scan.sync.sync_graph")
|
||||
@patch("tasks.jobs.attack_paths.scan.graph_database.drop_subgraph")
|
||||
@patch("tasks.jobs.attack_paths.scan.sync.create_sync_indexes")
|
||||
@patch("tasks.jobs.attack_paths.scan.internet.analysis")
|
||||
@patch("tasks.jobs.attack_paths.scan.findings.analysis")
|
||||
@patch("tasks.jobs.attack_paths.scan.findings.create_findings_indexes")
|
||||
@patch("tasks.jobs.attack_paths.scan.cartography_ontology.run")
|
||||
@@ -67,6 +69,7 @@ class TestAttackPathsRun:
|
||||
mock_cartography_ontology,
|
||||
mock_findings_indexes,
|
||||
mock_findings_analysis,
|
||||
mock_internet_analysis,
|
||||
mock_sync_indexes,
|
||||
mock_drop_subgraph,
|
||||
mock_sync,
|
||||
@@ -139,6 +142,7 @@ class TestAttackPathsRun:
|
||||
# These use tmp_cartography_config (neo4j_database="db-scan-id")
|
||||
mock_cartography_analysis.assert_called_once()
|
||||
mock_cartography_ontology.assert_called_once()
|
||||
mock_internet_analysis.assert_called_once()
|
||||
mock_findings_analysis.assert_called_once()
|
||||
mock_drop_subgraph.assert_called_once_with(
|
||||
database="tenant-db",
|
||||
@@ -207,6 +211,7 @@ class TestAttackPathsRun:
|
||||
patch("tasks.jobs.attack_paths.scan.cartography_create_indexes.run"),
|
||||
patch("tasks.jobs.attack_paths.scan.cartography_analysis.run"),
|
||||
patch("tasks.jobs.attack_paths.scan.findings.create_findings_indexes"),
|
||||
patch("tasks.jobs.attack_paths.scan.internet.analysis"),
|
||||
patch("tasks.jobs.attack_paths.scan.findings.analysis"),
|
||||
patch(
|
||||
"tasks.jobs.attack_paths.scan.db_utils.retrieve_attack_paths_scan",
|
||||
@@ -757,3 +762,45 @@ class TestAttackPathsFindingsHelpers:
|
||||
findings_module.load_findings(mock_session, empty_gen(), provider, config)
|
||||
|
||||
mock_session.run.assert_not_called()
|
||||
|
||||
|
||||
class TestInternetAnalysis:
|
||||
def _make_provider_and_config(self):
|
||||
provider = MagicMock()
|
||||
provider.provider = "aws"
|
||||
provider.uid = "123456789012"
|
||||
config = SimpleNamespace(update_tag=1234567890)
|
||||
return provider, config
|
||||
|
||||
def test_analysis_creates_node_and_relationships(self):
|
||||
"""Verify both Cypher statements are executed and relationship count returned."""
|
||||
mock_session = MagicMock()
|
||||
mock_result = MagicMock()
|
||||
mock_result.single.return_value = {"relationships_merged": 3}
|
||||
mock_session.run.side_effect = [None, mock_result]
|
||||
provider, config = self._make_provider_and_config()
|
||||
|
||||
with patch(
|
||||
"tasks.jobs.attack_paths.internet.get_root_node_label",
|
||||
return_value="AWSAccount",
|
||||
):
|
||||
result = internet_module.analysis(mock_session, provider, config)
|
||||
|
||||
assert mock_session.run.call_count == 2
|
||||
assert result == 3
|
||||
|
||||
def test_analysis_zero_exposed_resources(self):
|
||||
"""When no resources are exposed, zero relationships are created."""
|
||||
mock_session = MagicMock()
|
||||
mock_result = MagicMock()
|
||||
mock_result.single.return_value = {"relationships_merged": 0}
|
||||
mock_session.run.side_effect = [None, mock_result]
|
||||
provider, config = self._make_provider_and_config()
|
||||
|
||||
with patch(
|
||||
"tasks.jobs.attack_paths.internet.get_root_node_label",
|
||||
return_value="AWSAccount",
|
||||
):
|
||||
result = internet_module.analysis(mock_session, provider, config)
|
||||
|
||||
assert result == 0
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
examples
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
@@ -0,0 +1,12 @@
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
repository: oci://registry-1.docker.io/bitnamicharts
|
||||
version: 18.2.0
|
||||
- name: valkey
|
||||
repository: https://valkey.io/valkey-helm/
|
||||
version: 0.9.3
|
||||
- name: neo4j
|
||||
repository: https://helm.neo4j.com/neo4j
|
||||
version: 2025.12.1
|
||||
digest: sha256:da19233c6832727345fcdb314d683d30aa347d349f270023f3a67149bffb009b
|
||||
generated: "2026-01-26T12:00:06.798702+02:00"
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: v2
|
||||
name: prowler
|
||||
description: Prowler is an Open Cloud Security tool for AWS, Azure, GCP and Kubernetes. It helps for continuous monitoring, security assessments and audits, incident response, compliance, hardening and forensics readiness.
|
||||
type: application
|
||||
version: 0.0.1
|
||||
appVersion: "5.17.0"
|
||||
home: https://prowler.com
|
||||
icon: https://cdn.prod.website-files.com/68c4ec3f9fb7b154fbcb6e36/68c5e0fea5d0059b9e05834b_Link.png
|
||||
keywords:
|
||||
- security
|
||||
- aws
|
||||
- azure
|
||||
- gcp
|
||||
- kubernetes
|
||||
maintainers:
|
||||
- name: Mihai
|
||||
email: mihai.legat@gmail.com
|
||||
dependencies:
|
||||
# https://artifacthub.io/packages/helm/bitnami/postgresql
|
||||
- name: postgresql
|
||||
version: 18.2.0
|
||||
repository: oci://registry-1.docker.io/bitnamicharts
|
||||
condition: postgresql.enabled
|
||||
# https://valkey.io/valkey-helm/
|
||||
- name: valkey
|
||||
version: 0.9.3
|
||||
repository: https://valkey.io/valkey-helm/
|
||||
condition: valkey.enabled
|
||||
# https://helm.neo4j.com/neo4j
|
||||
- name: neo4j
|
||||
version: 2025.12.1
|
||||
repository: https://helm.neo4j.com/neo4j
|
||||
condition: neo4j.enabled
|
||||
@@ -0,0 +1,143 @@
|
||||
<!--
|
||||
This README is the one shown on Artifact Hub.
|
||||
Images should use absolute URLs.
|
||||
-->
|
||||
|
||||
# Prowler App Helm Chart
|
||||
|
||||

|
||||

|
||||
|
||||
Prowler is an Open Cloud Security tool for AWS, Azure, GCP and Kubernetes. It helps for continuous monitoring, security assessments and audits, incident response, compliance, hardening and forensics readiness. Includes CIS, NIST 800, NIST CSF, CISA, FedRAMP, PCI-DSS, GDPR, HIPAA, FFIEC, SOC2, GXP, Well-Architected Security, ENS and more.
|
||||
|
||||
## Architecture
|
||||
|
||||
The Prowler App consists of three main components:
|
||||
|
||||
- **Prowler UI**: A user-friendly web interface for running Prowler and viewing results, powered by Next.js.
|
||||
- **Prowler API**: The backend API that executes Prowler scans and stores the results, built with Django REST Framework.
|
||||
- **Prowler SDK**: A Python SDK that integrates with the Prowler CLI for advanced functionality.
|
||||
|
||||
The app leverages the following supporting infrastructure:
|
||||
|
||||
- **PostgreSQL**: Used for persistent storage of scan results.
|
||||
- **Celery Workers**: Facilitate asynchronous execution of Prowler scans.
|
||||
- **Valkey**: An in-memory database serving as a message broker for the Celery workers.
|
||||
- **Neo4j**: Graph Database
|
||||
- **Keda**: Kubernetes Event-driven Autoscaling (Keda) automatically scales the number of Celery worker pods based on the workload, ensuring efficient resource utilization and responsiveness.
|
||||
|
||||
## Setup
|
||||
|
||||
This guide walks you through installing Prowler App using Helm. For a minimal installation example, see the [minimal installation example](./examples/minimal-installation/).
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Kubernetes cluster (1.24+)
|
||||
- Helm 3.x installed
|
||||
- `kubectl` configured to access your cluster
|
||||
- Access to the Prowler Helm chart repository (or local chart)
|
||||
|
||||
### Step 1: Create Required Secrets
|
||||
|
||||
Before installing the Helm chart, you must create a Kubernetes Secret containing the required authentication keys and secrets.
|
||||
|
||||
1. **Generate the required keys and secrets:**
|
||||
|
||||
```bash
|
||||
# Generate Django token signing key (private key)
|
||||
openssl genrsa -out private.pem 2048
|
||||
|
||||
# Generate Django token verifying key (public key)
|
||||
openssl rsa -in private.pem -pubout -out public.pem
|
||||
|
||||
# Generate Django secrets encryption key
|
||||
openssl rand -base64 32
|
||||
|
||||
# Generate Auth secret
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
2. **Create the secret file:**
|
||||
|
||||
Create a file named `secrets.yaml` with the following structure:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: Opaque
|
||||
metadata:
|
||||
name: prowler-secret
|
||||
stringData:
|
||||
DJANGO_TOKEN_SIGNING_KEY: |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
[paste your private key here]
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
DJANGO_TOKEN_VERIFYING_KEY: |
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
[paste your public key here]
|
||||
-----END PUBLIC KEY-----
|
||||
|
||||
DJANGO_SECRETS_ENCRYPTION_KEY: "[paste your encryption key here]"
|
||||
|
||||
AUTH_SECRET: "[paste your auth secret here]"
|
||||
|
||||
NEO4J_PASSWORD: "[prowler-password]"
|
||||
NEO4J_AUTH: "neo4j/[prowler-password]"
|
||||
```
|
||||
|
||||
> **Note:** You can use the [example secrets file](./examples/minimal-installation/secrets.yaml) as a template, but **always replace the placeholder values with your own secure keys** before applying.
|
||||
|
||||
3. **Apply the secret to your cluster:**
|
||||
|
||||
```bash
|
||||
kubectl apply -f secrets.yaml
|
||||
```
|
||||
|
||||
### Step 2: Configure Values
|
||||
|
||||
Create a `values.yaml` file to customize your installation. At minimum, you need to configure the UI access method.
|
||||
|
||||
**Option A: Using Ingress (Recommended for production)**
|
||||
|
||||
```yaml
|
||||
ui:
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- host: prowler.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: ImplementationSpecific
|
||||
```
|
||||
|
||||
**Option B: Using authUrl (For proxy setups)**
|
||||
|
||||
```yaml
|
||||
ui:
|
||||
authUrl: prowler.example.com
|
||||
```
|
||||
|
||||
> **Note:** See the [minimal installation example](./examples/minimal-installation/values.yaml) for a complete reference.
|
||||
|
||||
### Step 3: Install the Chart
|
||||
|
||||
Install Prowler App using Helm:
|
||||
|
||||
```bash
|
||||
helm dependency update
|
||||
helm install prowler prowler/prowler-app -f values.yaml
|
||||
```
|
||||
|
||||
### Using Existing PostgreSQL and Valkey Instances
|
||||
|
||||
By default, this Chart uses Bitnami's Charts to deploy [PostgreSQL](https://artifacthub.io/packages/helm/bitnami/postgresql), [Neo4j](https://helm.neo4j.com/neo4j) and [Valkey official helm chart](https://valkey.io/valkey-helm/). **Note:** This default setup is not production-ready.
|
||||
|
||||
To connect to existing PostgreSQL, Neo4j and Valkey instances:
|
||||
|
||||
1. Create a `Secret` containing the correct database and message broker credentials
|
||||
2. Reference the secret in the [values.yaml](values.yaml) file api->secrets list
|
||||
|
||||
## Contributing
|
||||
|
||||
Feel free to contact the maintainer of this repository for any questions or concerns. Contributions are encouraged and appreciated.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Minimal Installation Example
|
||||
|
||||
This example demonstrates a minimal installation of Prowler in a Kubernetes cluster.
|
||||
|
||||
## Installation
|
||||
|
||||
To install Prowler using this example:
|
||||
|
||||
1. First, create the required secret:
|
||||
```bash
|
||||
# Edit secret.yaml and set secure values before applying
|
||||
kubectl apply -f secret.yaml
|
||||
```
|
||||
|
||||
1. Install the chart using the base values file:
|
||||
```bash
|
||||
# Basic installation
|
||||
helm install prowler prowler/prowler-app -f values.yaml
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The example contains the following configuration files:
|
||||
|
||||
### `secret.yaml`
|
||||
Contains all required secrets for the Prowler installation. **Must be applied before installing the Helm chart**. Make sure to replace all placeholder values with secure values before applying.
|
||||
|
||||
### `values.yaml`
|
||||
```yaml
|
||||
ui:
|
||||
# Note: You should set either `authUrl` if you use prowler behind a proxy or enable `ingress`.
|
||||
|
||||
# Example with authUrl:
|
||||
# authUrl: example.prowler.com
|
||||
|
||||
# Example with ingress:
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- host: example.prowler.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: ImplementationSpecific
|
||||
```
|
||||
|
||||
Make sure to adjust the hostname in the values file to match your environment before installing.
|
||||
@@ -0,0 +1,58 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
type: Opaque
|
||||
metadata:
|
||||
name: prowler-secret
|
||||
stringData:
|
||||
# openssl genrsa -out private.pem 2048
|
||||
DJANGO_TOKEN_SIGNING_KEY: |
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCIro0QiLAxw7rF
|
||||
GO0NgAWJfkpYE5ysMGDCbId07HUrv+/SCoRjqKVzGJVIvmNP5oByzSehPgswW9v3
|
||||
3dqe2r9sCS1JyMa+XO3qfZCR0uRDcPCwZjIyr0QQLpWAymdBa8baeHsU1/3Orjcb
|
||||
Vrr+lNx4HQJOiSn094iXPReW/25hYeq/SXs79V2CR87PGdoZAhb8IllAxJgdfkeB
|
||||
/iWohY/1vfRTmIuMweWGXk0aKzPsBdvE/DqG4HjiNVEPh18G3vid0YTZNmm7u8vO
|
||||
Cue3x9NQWGHA4QtxNtLtxlHcOEryqZ9ChO2nC+ew0Xl/v706XFNyLFicjisIKNQo
|
||||
qdkaMS33AgMBAAECggEAGdJIChCYoL4mYafk2MEPyrrWFq+V0J3PGcvhB0DInfxD
|
||||
tT2RZzZsE0NYqIZ3Qpf8OjPxwa9z863W74u1Cn+u3B0bti29BieONteD4VijEO6c
|
||||
OecEorijth7m1Y7nVN+kkI9kSTrI0yvsczi+WOwMfpCUZ/vXtlSxNEkxVLBqzPCo
|
||||
9VxAFIjgWOj2rpw8nxPedves36PUrC5ghLqrOTe1jmw/Di0++47AXG+DsTXc00sc
|
||||
5+oybopm3Kimsxrqbf9s8SZf2A8NiwqcbLj8OtP2j2g4TCEgZYLD5Zmt+JN/wN4B
|
||||
WsQG/Hwp4KPPm9QTHEpuuoPFP1CZWZeq8gPcV4apYQKBgQC+TuXjJCYhZqNIttTZ
|
||||
z/i3hkKUEKQLkzTZnXaDzL5wHyEMVqM2E/WkilO0C9ZZwh0ENPzkp+JsHf7LEhHy
|
||||
wSHOti81VzUCjN/YpCBKlOlClqSiDlOonImrobLei8xgvmA0VmGtirCXZyyzZUoV
|
||||
OyPr17WpK6G/M5piX59MvKQg0QKBgQC33NBoQFD8A6FjrTopYmWfK099k9uQh9NE
|
||||
bvUYsNAPunSDslmc/0PPHQC7fRX5Ime2BinXAN1PYtB/Fsu3jv/+FCUM5hVil0Dd
|
||||
KBvt13+RYSCJKlhcGP1EkWoIg1F2XXBOZKJrC8VQ+Vyl2t06UcWQqy5M9J4VZaqI
|
||||
fruOLU/URwKBgE55GjJfZZnASPRi78IhD94dbra/ZeWf/dr+IzCV7LEvJOGBmCtk
|
||||
b5Y5s+o6N1krwetKLj3bPHJ4q+fwu5XuLZKfbTgBjcpPbL5YbzhRzx22IIzye2y7
|
||||
n8k2FBvQaaY62lC6jeyRk9/am4Qd8D5w9I77k9z+MOQ20yJda8KoxsUBAoGBAIQ9
|
||||
5QPmppjsf4ry0C9t30uhWhYnX7fPiYviBpVQrwVxBVan076Q9xOjd6BicohzT4bj
|
||||
XfqPW546o12VZsbKqqLzmEZzwpPb2EJ5E8V4xv8ojb86Xr03GArWUB55XQE2aY1o
|
||||
4kz99VitUg7UoWPN5ryL8sxU8NLRAdwU0w+K1a0HAoGAZaU7O94u9IIPZ6Ohobs2
|
||||
Vjf/eV0brCKgX61b4z/YhuJdZsyTujhBZUihZwqR696kiFKuzmHx1ghE2ITvnPVN
|
||||
q0iHxRZzBCnRQ+mQlS0trzphaCP0NVy3osFeAD9mJfnOnSmkU0ua4F81mkvke1eN
|
||||
6nnaoAdy2lmMr96/Tye2ty4=
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
# openssl rsa -in private.pem -pubout -out public.pem
|
||||
DJANGO_TOKEN_VERIFYING_KEY: |
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiK6NEIiwMcO6xRjtDYAF
|
||||
iX5KWBOcrDBgwmyHdOx1K7/v0gqEY6ilcxiVSL5jT+aAcs0noT4LMFvb993antq/
|
||||
bAktScjGvlzt6n2QkdLkQ3DwsGYyMq9EEC6VgMpnQWvG2nh7FNf9zq43G1a6/pTc
|
||||
eB0CTokp9PeIlz0Xlv9uYWHqv0l7O/VdgkfOzxnaGQIW/CJZQMSYHX5Hgf4lqIWP
|
||||
9b30U5iLjMHlhl5NGisz7AXbxPw6huB44jVRD4dfBt74ndGE2TZpu7vLzgrnt8fT
|
||||
UFhhwOELcTbS7cZR3DhK8qmfQoTtpwvnsNF5f7+9OlxTcixYnI4rCCjUKKnZGjEt
|
||||
9wIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
|
||||
# openssl rand -base64 32
|
||||
DJANGO_SECRETS_ENCRYPTION_KEY: "qYAIWnRK52aBT5YQkBoMEw08j7j3+QIPZXS6+A8Su44="
|
||||
|
||||
# openssl rand -base64 32
|
||||
AUTH_SECRET: "CM9w3Nco2P1RdHaYmD+fmy2nJmSofusdHd4g7Z4KDG4="
|
||||
|
||||
# Unfortunatelly, we need to duplicate the password in two different keys because the Neo4j Helm Chart expects the password in the NEO4J_AUTH key and the application expects it in the NEO4J_PASSWORD key.
|
||||
NEO4J_PASSWORD: "prowler-password-fake"
|
||||
NEO4J_AUTH: "neo4j/prowler-password-fake"
|
||||
@@ -0,0 +1,11 @@
|
||||
ui:
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- host: 127.0.0.1.nip.io
|
||||
paths:
|
||||
- path: /
|
||||
pathType: ImplementationSpecific
|
||||
|
||||
# or use authUrl if you use prowler behind a proxy
|
||||
# authUrl: 127.0.0.1.nip.io
|
||||
@@ -0,0 +1,134 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "prowler.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "prowler.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "prowler.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "prowler.labels" -}}
|
||||
helm.sh/chart: {{ include "prowler.chart" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Django environment variables for api, worker, and worker_beat.
|
||||
*/}}
|
||||
{{- define "prowler.django.env" -}}
|
||||
- name: DJANGO_TOKEN_SIGNING_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.djangoTokenSigningKey.secretKeyRef.name }}
|
||||
key: {{ .Values.djangoTokenSigningKey.secretKeyRef.key }}
|
||||
- name: DJANGO_TOKEN_VERIFYING_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.djangoTokenVerifyingKey.secretKeyRef.name }}
|
||||
key: {{ .Values.djangoTokenVerifyingKey.secretKeyRef.key }}
|
||||
- name: DJANGO_SECRETS_ENCRYPTION_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.djangoSecretsEncryptionKey.secretKeyRef.name }}
|
||||
key: {{ .Values.djangoSecretsEncryptionKey.secretKeyRef.key }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/*
|
||||
PostgreSQL environment variables for api, worker, and worker_beat.
|
||||
Outputs nothing when postgresql.enabled is false.
|
||||
*/}}
|
||||
{{- define "prowler.postgresql.env" -}}
|
||||
{{- if .Values.postgresql.enabled }}
|
||||
{{- if .Values.postgresql.auth.username }}
|
||||
- name: POSTGRES_USER
|
||||
value: {{ .Values.postgresql.auth.username | quote }}
|
||||
{{- end }}
|
||||
- name: POSTGRES_PASSWORD
|
||||
{{- if .Values.postgresql.auth.existingSecret }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.postgresql.auth.existingSecret }}
|
||||
key: {{ required "postgresql.auth.secretKeys.userPasswordKey is required when using an existing secret" .Values.postgresql.auth.secretKeys.userPasswordKey }}
|
||||
{{- else if .Values.postgresql.auth.password }}
|
||||
value: {{ .Values.postgresql.auth.password | quote }}
|
||||
{{- else }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgresql
|
||||
key: password
|
||||
{{- end }}
|
||||
- name: POSTGRES_DB
|
||||
value: {{ .Values.postgresql.auth.database | quote }}
|
||||
- name: POSTGRES_HOST
|
||||
value: {{ .Release.Name }}-postgresql
|
||||
- name: POSTGRES_PORT
|
||||
value: "5432"
|
||||
- name: POSTGRES_ADMIN_USER
|
||||
value: postgres
|
||||
- name: POSTGRES_ADMIN_PASSWORD
|
||||
{{- if .Values.postgresql.auth.existingSecret }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.postgresql.auth.existingSecret }}
|
||||
key: {{ required "postgresql.auth.secretKeys.adminPasswordKey is required when using an existing secret" .Values.postgresql.auth.secretKeys.adminPasswordKey }}
|
||||
{{- else if .Values.postgresql.auth.postgresPassword }}
|
||||
value: {{ .Values.postgresql.auth.postgresPassword | quote }}
|
||||
{{- else }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgresql
|
||||
key: postgres-password
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Neo4j environment variables for api, worker, and worker_beat.
|
||||
Outputs nothing when neo4j.enabled is false.
|
||||
*/}}
|
||||
{{- define "prowler.neo4j.env" -}}
|
||||
{{- if .Values.neo4j.enabled }}
|
||||
- name: NEO4J_HOST
|
||||
value: {{ .Release.Name }}
|
||||
- name: NEO4J_PORT
|
||||
value: "7687"
|
||||
- name: NEO4J_USER
|
||||
value: "neo4j"
|
||||
- name: NEO4J_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ required "neo4j.neo4j.passwordFromSecret is required" .Values.neo4j.neo4j.passwordFromSecret }}
|
||||
key: NEO4J_PASSWORD
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,10 @@
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "prowler.api.serviceAccountName" -}}
|
||||
{{- if .Values.api.serviceAccount.create }}
|
||||
{{- default (printf "%s-%s" (include "prowler.fullname" .) "api") .Values.api.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.api.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,10 @@
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
data:
|
||||
{{- range $key, $value := .Values.api.djangoConfig }}
|
||||
{{ $key }}: {{ $value | quote }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,105 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.api.autoscaling.enabled }}
|
||||
replicas: {{ .Values.api.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "prowler.fullname" . }}-api
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
secret-hash: "{{ printf "%s%s%s" (.Files.Get "templates/api/configmap.yaml" | sha256sum) (.Files.Get "templates/api/secret-valkey.yaml" | sha256sum) | sha256sum }}"
|
||||
{{- with .Values.api.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 8 }}
|
||||
app.kubernetes.io/name: {{ include "prowler.fullname" . }}-api
|
||||
{{- with .Values.api.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.api.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "prowler.api.serviceAccountName" . }}
|
||||
{{- with .Values.api.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: api
|
||||
{{- with .Values.api.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
image: "{{ .Values.api.image.repository }}:{{ .Values.api.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.api.image.pullPolicy }}
|
||||
{{- with .Values.api.command }}
|
||||
command:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.api.args }}
|
||||
args:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.api.service.port }}
|
||||
protocol: TCP
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
{{- if .Values.valkey.enabled }}
|
||||
- secretRef:
|
||||
name: {{ include "prowler.fullname" . }}-api-valkey
|
||||
{{- end }}
|
||||
{{- with .Values.api.secrets }}
|
||||
{{- range $index, $secret := . }}
|
||||
- secretRef:
|
||||
name: {{ $secret }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
env:
|
||||
{{- include "prowler.django.env" . | nindent 12 }}
|
||||
{{- include "prowler.postgresql.env" . | nindent 12 }}
|
||||
{{- include "prowler.neo4j.env" . | nindent 12 }}
|
||||
{{- with .Values.api.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.api.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.api.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.api.volumeMounts }}
|
||||
volumeMounts:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.api.volumes }}
|
||||
volumes:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.api.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.api.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.api.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,32 @@
|
||||
{{- if .Values.api.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
minReplicas: {{ .Values.api.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.api.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.api.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.api.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.api.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.api.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,43 @@
|
||||
{{- if .Values.api.ingress.enabled -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
{{- with .Values.api.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.api.ingress.className }}
|
||||
ingressClassName: {{ . }}
|
||||
{{- end }}
|
||||
{{- if .Values.api.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.api.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.api.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
{{- with .pathType }}
|
||||
pathType: {{ . }}
|
||||
{{- end }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "prowler.fullname" $ }}-api
|
||||
port:
|
||||
number: {{ $.Values.api.service.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,29 @@
|
||||
# https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/prowler-app/#step-44-kubernetes-credentials
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "configmaps", "nodes", "namespaces"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||
resources: ["clusterrolebindings", "rolebindings", "clusterroles", "roles"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "prowler.api.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
@@ -0,0 +1,13 @@
|
||||
{{- if .Values.valkey.enabled -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-api-valkey
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
VALKEY_HOST: "{{ include "prowler.fullname" . }}-valkey"
|
||||
VALKEY_PORT: "6379"
|
||||
VALKEY_DB: "0"
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.api.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.api.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ include "prowler.fullname" . }}-api
|
||||
@@ -0,0 +1,13 @@
|
||||
{{- if .Values.api.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "prowler.api.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
{{- with .Values.api.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
automountServiceAccountToken: {{ .Values.api.serviceAccount.automount }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,10 @@
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "prowler.ui.serviceAccountName" -}}
|
||||
{{- if .Values.ui.serviceAccount.create }}
|
||||
{{- default (printf "%s-%s" (include "prowler.fullname" .) "ui") .Values.ui.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.ui.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,18 @@
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-ui
|
||||
data:
|
||||
PROWLER_UI_VERSION: "stable"
|
||||
{{- if .Values.ui.ingress.enabled }}
|
||||
{{- with (first .Values.ui.ingress.hosts) }}
|
||||
AUTH_URL: "https://{{ .host }}"
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
AUTH_URL: {{ .Values.ui.authUrl | quote }}
|
||||
{{- end }}
|
||||
API_BASE_URL: "http://{{ include "prowler.fullname" . }}-api:{{ .Values.api.service.port }}/api/v1"
|
||||
NEXT_PUBLIC_API_BASE_URL: "http://{{ include "prowler.fullname" . }}-api:{{ .Values.api.service.port }}/api/v1"
|
||||
NEXT_PUBLIC_API_DOCS_URL: "http://{{ include "prowler.fullname" . }}-api:{{ .Values.api.service.port }}/api/v1/docs"
|
||||
AUTH_TRUST_HOST: "true"
|
||||
UI_PORT: {{ .Values.ui.service.port | quote }}
|
||||
@@ -0,0 +1,95 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-ui
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.ui.autoscaling.enabled }}
|
||||
replicas: {{ .Values.ui.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "prowler.fullname" . }}-ui
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
secret-hash: {{ .Files.Get "templates/ui/configmap.yaml" | sha256sum }}
|
||||
{{- with .Values.ui.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 8 }}
|
||||
app.kubernetes.io/name: {{ include "prowler.fullname" . }}-ui
|
||||
{{- with .Values.ui.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.ui.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "prowler.ui.serviceAccountName" . }}
|
||||
{{- with .Values.ui.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: ui
|
||||
{{- with .Values.ui.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
image: "{{ .Values.ui.image.repository }}:{{ .Values.ui.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.ui.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.ui.service.port }}
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: AUTH_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Values.ui.authSecret.secretKeyRef.name }}
|
||||
key: {{ .Values.ui.authSecret.secretKeyRef.key }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "prowler.fullname" . }}-ui
|
||||
{{- with .Values.ui.secrets }}
|
||||
{{- range $index, $secret := . }}
|
||||
- secretRef:
|
||||
name: {{ $secret }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.ui.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ui.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ui.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ui.volumeMounts }}
|
||||
volumeMounts:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ui.volumes }}
|
||||
volumes:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ui.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ui.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.ui.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,32 @@
|
||||
{{- if .Values.ui.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-ui
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "prowler.fullname" . }}-ui
|
||||
minReplicas: {{ .Values.ui.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.ui.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.ui.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.ui.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.ui.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.ui.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,43 @@
|
||||
{{- if .Values.ui.ingress.enabled -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-ui
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
{{- with .Values.ui.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.ui.ingress.className }}
|
||||
ingressClassName: {{ . }}
|
||||
{{- end }}
|
||||
{{- if .Values.ui.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ui.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ui.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
{{- with .pathType }}
|
||||
pathType: {{ . }}
|
||||
{{- end }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "prowler.fullname" $ }}-ui
|
||||
port:
|
||||
number: {{ $.Values.ui.service.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-ui
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.ui.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.ui.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ include "prowler.fullname" . }}-ui
|
||||
@@ -0,0 +1,13 @@
|
||||
{{- if .Values.ui.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "prowler.ui.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
{{- with .Values.ui.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
automountServiceAccountToken: {{ .Values.ui.serviceAccount.automount }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,10 @@
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "prowler.worker.serviceAccountName" -}}
|
||||
{{- if .Values.worker.serviceAccount.create }}
|
||||
{{- default (printf "%s-%s" (include "prowler.fullname" .) "worker") .Values.worker.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.worker.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,101 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-worker
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.worker.autoscaling.enabled }}
|
||||
replicas: {{ .Values.worker.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "prowler.fullname" . }}-worker
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
secret-hash: "{{ printf "%s%s%s" (.Files.Get "templates/api/configmap.yaml" | sha256sum) (.Files.Get "templates/api/secret-valkey.yaml" | sha256sum) | sha256sum }}"
|
||||
{{- with .Values.worker.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 8 }}
|
||||
app.kubernetes.io/name: {{ include "prowler.fullname" . }}-worker
|
||||
{{- with .Values.worker.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.worker.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "prowler.worker.serviceAccountName" . }}
|
||||
{{- with .Values.worker.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: worker
|
||||
{{- with .Values.worker.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
image: "{{ .Values.worker.image.repository }}:{{ .Values.worker.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.worker.image.pullPolicy }}
|
||||
{{- with .Values.worker.command }}
|
||||
command:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker.args }}
|
||||
args:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
{{- if .Values.valkey.enabled }}
|
||||
- secretRef:
|
||||
name: {{ include "prowler.fullname" . }}-api-valkey
|
||||
{{- end }}
|
||||
{{- with .Values.api.secrets }}
|
||||
{{- range $index, $secret := . }}
|
||||
- secretRef:
|
||||
name: {{ $secret }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
env:
|
||||
{{- include "prowler.django.env" . | nindent 12 }}
|
||||
{{- include "prowler.postgresql.env" . | nindent 12 }}
|
||||
{{- include "prowler.neo4j.env" . | nindent 12 }}
|
||||
{{- with .Values.worker.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker.volumeMounts }}
|
||||
volumeMounts:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker.volumes }}
|
||||
volumes:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,32 @@
|
||||
{{- if .Values.worker.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-worker
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "prowler.fullname" . }}-worker
|
||||
minReplicas: {{ .Values.worker.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.worker.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.worker.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.worker.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.worker.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.worker.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,32 @@
|
||||
{{- if .Values.worker.keda.enabled }}
|
||||
apiVersion: keda.sh/v1alpha1
|
||||
kind: ScaledObject
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-worker
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
name: {{ include "prowler.fullname" . }}-worker
|
||||
envSourceContainerName: worker
|
||||
kind: Deployment
|
||||
minReplicaCount: {{ .Values.worker.keda.minReplicas }}
|
||||
maxReplicaCount: {{ .Values.worker.keda.maxReplicas }}
|
||||
pollingInterval: {{ .Values.worker.keda.pollingInterval }}
|
||||
cooldownPeriod: {{ .Values.worker.keda.cooldownPeriod }}
|
||||
triggers:
|
||||
- type: {{ .Values.worker.keda.triggerType }}
|
||||
metadata:
|
||||
userName: "postgres"
|
||||
passwordFromEnv: POSTGRES_ADMIN_PASSWORD
|
||||
host: {{ .Release.Name }}-postgresql
|
||||
port: {{ .Values.postgresql.port | quote }}
|
||||
dbName: {{ .Values.postgresql.auth.database | quote }}
|
||||
sslmode: disable
|
||||
# Query for KEDA to count the number of scans that are in executing, available, or scheduled states,
|
||||
# where the scheduled time is within the last 2 hours and is before NOW(). Used for scaling workers.
|
||||
query: >-
|
||||
SELECT COUNT(*) FROM scans WHERE ((state='executing' OR state='available' OR state='scheduled') and scheduled_at < NOW() and scheduled_at > NOW() - INTERVAL '2 hours')
|
||||
targetQueryValue: "1"
|
||||
{{- end }}
|
||||
@@ -0,0 +1,13 @@
|
||||
{{- if .Values.worker.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "prowler.worker.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
{{- with .Values.worker.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
automountServiceAccountToken: {{ .Values.worker.serviceAccount.automount }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,10 @@
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "prowler.worker_beat.serviceAccountName" -}}
|
||||
{{- if .Values.worker_beat.serviceAccount.create }}
|
||||
{{- default (printf "%s-%s" (include "prowler.fullname" .) "worker-beat") .Values.worker_beat.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.worker_beat.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,99 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "prowler.fullname" . }}-worker-beat
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.worker_beat.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "prowler.fullname" . }}-worker-beat
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
secret-hash: "{{ printf "%s%s%s" (.Files.Get "templates/api/configmap.yaml" | sha256sum) (.Files.Get "templates/api/secret-valkey.yaml" | sha256sum) | sha256sum }}"
|
||||
{{- with .Values.worker.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 8 }}
|
||||
app.kubernetes.io/name: {{ include "prowler.fullname" . }}-worker-beat
|
||||
{{- with .Values.worker_beat.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.worker_beat.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "prowler.worker_beat.serviceAccountName" . }}
|
||||
{{- with .Values.worker_beat.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: worker-beat
|
||||
{{- with .Values.worker_beat.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
image: "{{ .Values.worker_beat.image.repository }}:{{ .Values.worker_beat.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.worker_beat.image.pullPolicy }}
|
||||
{{- with .Values.worker_beat.command }}
|
||||
command:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker_beat.args }}
|
||||
args:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "prowler.fullname" . }}-api
|
||||
{{- if .Values.valkey.enabled }}
|
||||
- secretRef:
|
||||
name: {{ include "prowler.fullname" . }}-api-valkey
|
||||
{{- end }}
|
||||
{{- with .Values.api.secrets }}
|
||||
{{- range $index, $secret := . }}
|
||||
- secretRef:
|
||||
name: {{ $secret }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
env:
|
||||
{{- include "prowler.django.env" . | nindent 12 }}
|
||||
{{- include "prowler.postgresql.env" . | nindent 12 }}
|
||||
{{- include "prowler.neo4j.env" . | nindent 12 }}
|
||||
{{- with .Values.worker_beat.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker_beat.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker_beat.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker_beat.volumeMounts }}
|
||||
volumeMounts:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker_beat.volumes }}
|
||||
volumes:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker_beat.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker_beat.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.worker_beat.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,13 @@
|
||||
{{- if .Values.worker_beat.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "prowler.worker_beat.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "prowler.labels" . | nindent 4 }}
|
||||
{{- with .Values.worker_beat.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
automountServiceAccountToken: {{ .Values.worker_beat.serviceAccount.automount }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,566 @@
|
||||
# This is to override the chart name.
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
# Reference to the secret containing the API authentication secret.
|
||||
# Used to inject the environment variable for the API container.
|
||||
djangoTokenSigningKey:
|
||||
secretKeyRef:
|
||||
name: prowler-secret
|
||||
key: DJANGO_TOKEN_SIGNING_KEY
|
||||
djangoTokenVerifyingKey:
|
||||
secretKeyRef:
|
||||
name: prowler-secret
|
||||
key: DJANGO_TOKEN_VERIFYING_KEY
|
||||
djangoSecretsEncryptionKey:
|
||||
secretKeyRef:
|
||||
name: prowler-secret
|
||||
key: DJANGO_SECRETS_ENCRYPTION_KEY
|
||||
|
||||
ui:
|
||||
# This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
|
||||
replicaCount: 1
|
||||
|
||||
# This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/
|
||||
image:
|
||||
repository: prowlercloud/prowler-ui
|
||||
# This sets the pull policy for images.
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
# This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
|
||||
imagePullSecrets: []
|
||||
|
||||
# Reference to the secret containing the UI authentication secret.
|
||||
# Used to inject the environment variable for the UI container.
|
||||
# By default, expects a Secret named 'prowler-secret' with a key 'AUTH_SECRET'.
|
||||
authSecret:
|
||||
secretKeyRef:
|
||||
name: prowler-secret
|
||||
key: AUTH_SECRET
|
||||
|
||||
# Secret names to be used as env vars.
|
||||
secrets: []
|
||||
# - "prowler-ui-secret"
|
||||
|
||||
# This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Automatically mount a ServiceAccount's API credentials?
|
||||
automount: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
# This is for setting Kubernetes Annotations to a Pod.
|
||||
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
|
||||
podAnnotations: {}
|
||||
# This is for setting Kubernetes Labels to a Pod.
|
||||
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
|
||||
podLabels: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
# This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/
|
||||
service:
|
||||
# This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
|
||||
type: ClusterIP
|
||||
# This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports
|
||||
port: 3000
|
||||
|
||||
# The URL of the UI. This is only set if ingress is disabled.
|
||||
authUrl: ""
|
||||
|
||||
# This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/
|
||||
ingress:
|
||||
enabled: false
|
||||
className: ""
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
hosts:
|
||||
- host: chart-example.local
|
||||
paths:
|
||||
- path: /
|
||||
pathType: ImplementationSpecific
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - chart-example.local
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
# This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
|
||||
# This section is for setting up autoscaling more information can be found here: https://kubernetes.io/docs/concepts/workloads/autoscaling/
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 100
|
||||
targetCPUUtilizationPercentage: 80
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
# Additional volumes on the output Deployment definition.
|
||||
volumes: []
|
||||
# - name: foo
|
||||
# secret:
|
||||
# secretName: mysecret
|
||||
# optional: false
|
||||
|
||||
# Additional volumeMounts on the output Deployment definition.
|
||||
volumeMounts: []
|
||||
# - name: foo
|
||||
# mountPath: "/etc/foo"
|
||||
# readOnly: true
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
|
||||
api:
|
||||
# This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
|
||||
replicaCount: 1
|
||||
|
||||
# This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/
|
||||
image:
|
||||
repository: prowlercloud/prowler-api
|
||||
# This sets the pull policy for images.
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
# Shared with celery-worker and celery-beat
|
||||
djangoConfig:
|
||||
# API scan settings
|
||||
# The path to the directory where scan output should be stored
|
||||
DJANGO_TMP_OUTPUT_DIRECTORY: "/tmp/prowler_api_output"
|
||||
# The maximum number of findings to process in a single batch
|
||||
DJANGO_FINDINGS_BATCH_SIZE: "1000"
|
||||
# Django settings
|
||||
DJANGO_ALLOWED_HOSTS: "*"
|
||||
DJANGO_BIND_ADDRESS: "0.0.0.0"
|
||||
DJANGO_PORT: "8080"
|
||||
DJANGO_DEBUG: "False"
|
||||
DJANGO_SETTINGS_MODULE: "config.django.production"
|
||||
# Select one of [ndjson|human_readable]
|
||||
DJANGO_LOGGING_FORMATTER: "ndjson"
|
||||
# Select one of [DEBUG|INFO|WARNING|ERROR|CRITICAL]
|
||||
# Applies to both Django and Celery Workers
|
||||
DJANGO_LOGGING_LEVEL: "INFO"
|
||||
# Defaults to the maximum available based on CPU cores if not set.
|
||||
DJANGO_WORKERS: "4"
|
||||
# Token lifetime is in minutes
|
||||
DJANGO_ACCESS_TOKEN_LIFETIME: "30"
|
||||
# Token lifetime is in minutes
|
||||
DJANGO_REFRESH_TOKEN_LIFETIME: "1440"
|
||||
DJANGO_CACHE_MAX_AGE: "3600"
|
||||
DJANGO_STALE_WHILE_REVALIDATE: "60"
|
||||
DJANGO_MANAGE_DB_PARTITIONS: "True"
|
||||
DJANGO_BROKER_VISIBILITY_TIMEOUT: "86400"
|
||||
|
||||
# Secret names to be used as env vars for api, worker, and worker_beat.
|
||||
secrets: []
|
||||
# - "prowler-api-keys"
|
||||
|
||||
command:
|
||||
- /home/prowler/docker-entrypoint.sh
|
||||
args:
|
||||
- prod
|
||||
|
||||
# This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
|
||||
imagePullSecrets: []
|
||||
|
||||
# This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Automatically mount a ServiceAccount's API credentials?
|
||||
automount: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
# This is for setting Kubernetes Annotations to a Pod.
|
||||
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
|
||||
podAnnotations: {}
|
||||
# This is for setting Kubernetes Labels to a Pod.
|
||||
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
|
||||
podLabels: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
# This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/
|
||||
service:
|
||||
# This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
|
||||
type: ClusterIP
|
||||
# This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports
|
||||
port: 8080
|
||||
|
||||
# This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/
|
||||
ingress:
|
||||
enabled: false
|
||||
className: ""
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
hosts:
|
||||
- host: chart-example.local
|
||||
paths:
|
||||
- path: /
|
||||
pathType: ImplementationSpecific
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - chart-example.local
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
# 3m30s to setup DB
|
||||
# startupProbe:
|
||||
# httpGet:
|
||||
# path: /api/v1/docs
|
||||
# port: http
|
||||
|
||||
# This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
|
||||
livenessProbe:
|
||||
failureThreshold: 10
|
||||
httpGet:
|
||||
path: /api/v1/docs
|
||||
port: http
|
||||
periodSeconds: 20
|
||||
readinessProbe:
|
||||
failureThreshold: 10
|
||||
httpGet:
|
||||
path: /api/v1/docs
|
||||
port: http
|
||||
periodSeconds: 20
|
||||
|
||||
# This section is for setting up autoscaling more information can be found here: https://kubernetes.io/docs/concepts/workloads/autoscaling/
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 100
|
||||
targetCPUUtilizationPercentage: 80
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
# Additional volumes on the output Deployment definition.
|
||||
volumes: []
|
||||
# - name: foo
|
||||
# secret:
|
||||
# secretName: mysecret
|
||||
# optional: false
|
||||
|
||||
# Additional volumeMounts on the output Deployment definition.
|
||||
volumeMounts: []
|
||||
# - name: foo
|
||||
# mountPath: "/etc/foo"
|
||||
# readOnly: true
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
|
||||
worker:
|
||||
# This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
|
||||
replicaCount: 1
|
||||
|
||||
# This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/
|
||||
image:
|
||||
repository: prowlercloud/prowler-api
|
||||
# This sets the pull policy for images.
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
command:
|
||||
- /home/prowler/docker-entrypoint.sh
|
||||
args:
|
||||
- worker
|
||||
|
||||
# This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
|
||||
imagePullSecrets: []
|
||||
|
||||
# This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Automatically mount a ServiceAccount's API credentials?
|
||||
automount: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
# This is for setting Kubernetes Annotations to a Pod.
|
||||
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
|
||||
podAnnotations: {}
|
||||
# This is for setting Kubernetes Labels to a Pod.
|
||||
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
|
||||
podLabels: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
# This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
|
||||
livenessProbe: {}
|
||||
readinessProbe: {}
|
||||
|
||||
# This section is for setting up autoscaling more information can be found here: https://kubernetes.io/docs/concepts/workloads/autoscaling/
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 10
|
||||
targetCPUUtilizationPercentage: 80
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
# Additional volumes on the output Deployment definition.
|
||||
volumes: []
|
||||
# - name: foo
|
||||
# secret:
|
||||
# secretName: mysecret
|
||||
# optional: false
|
||||
|
||||
# Additional volumeMounts on the output Deployment definition.
|
||||
volumeMounts: []
|
||||
# - name: foo
|
||||
# mountPath: "/etc/foo"
|
||||
# readOnly: true
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
|
||||
# KEDA ScaledObject configuration
|
||||
keda:
|
||||
# -- Set to `true` to enable KEDA for the worker pods
|
||||
# Note: When both KEDA and HPA are enabled, the deployment will fail.
|
||||
enabled: false
|
||||
# -- The minimum number of replicas to use for the worker pods
|
||||
minReplicas: 1
|
||||
# -- The maximum number of replicas to use for the worker pods
|
||||
maxReplicas: 2
|
||||
# -- The polling interval in seconds for checking metrics
|
||||
pollingInterval: 30
|
||||
# -- The cooldown period in seconds for scaling
|
||||
cooldownPeriod: 120
|
||||
# -- The trigger type for scaling (cpu or memory)
|
||||
triggerType: "postgresql"
|
||||
# -- The target utilization percentage for the worker pods
|
||||
value: "50"
|
||||
|
||||
worker_beat:
|
||||
# This will set the replicaset count more information can be found here: https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
|
||||
replicaCount: 1
|
||||
|
||||
# This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/
|
||||
image:
|
||||
repository: prowlercloud/prowler-api
|
||||
# This sets the pull policy for images.
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
command:
|
||||
- ../docker-entrypoint.sh
|
||||
args:
|
||||
- beat
|
||||
|
||||
# This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
|
||||
imagePullSecrets: []
|
||||
|
||||
# This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Automatically mount a ServiceAccount's API credentials?
|
||||
automount: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
# This is for setting Kubernetes Annotations to a Pod.
|
||||
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
|
||||
podAnnotations: {}
|
||||
# This is for setting Kubernetes Labels to a Pod.
|
||||
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
|
||||
podLabels: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
resources: {}
|
||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
||||
# choice for the user. This also increases chances charts run on environments with little
|
||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
||||
# limits:
|
||||
# memory: 128Mi
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
# This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
|
||||
livenessProbe: {}
|
||||
readinessProbe: {}
|
||||
|
||||
# Additional volumes on the output Deployment definition.
|
||||
volumes: []
|
||||
# - name: foo
|
||||
# secret:
|
||||
# secretName: mysecret
|
||||
# optional: false
|
||||
|
||||
# Additional volumeMounts on the output Deployment definition.
|
||||
volumeMounts: []
|
||||
# - name: foo
|
||||
# mountPath: "/etc/foo"
|
||||
# readOnly: true
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
|
||||
postgresql:
|
||||
# -- Enable PostgreSQL deployment (via Bitnami Helm Chart). If you want to use an external Postgres server (or a managed one), set this to false
|
||||
# If enabled, it will create a Secret with the credentials.
|
||||
# Otherwise, create a secret with the following and add it to the api deployment:
|
||||
# - POSTGRES_HOST
|
||||
# - POSTGRES_PORT
|
||||
# - POSTGRES_ADMIN_USER - Existing user in charge of migrations, tables, permissions, RLS
|
||||
# - POSTGRES_ADMIN_PASSWORD
|
||||
# - POSTGRES_USER - Will be created by ADMIN_USER
|
||||
# - POSTGRES_PASSWORD
|
||||
# - POSTGRES_DB - Existing DB
|
||||
enabled: true
|
||||
image:
|
||||
repository: "bitnami/postgresql"
|
||||
auth:
|
||||
database: prowler_db
|
||||
username: prowler
|
||||
|
||||
valkey:
|
||||
# If enabled, it will create a Secret with the following.
|
||||
# Otherwise, create a secret with
|
||||
# - VALKEY_HOST
|
||||
# - VALKEY_PORT
|
||||
# - VALKEY_DB
|
||||
enabled: true
|
||||
|
||||
neo4j:
|
||||
enabled: true
|
||||
|
||||
neo4j:
|
||||
name: prowler-neo4j
|
||||
edition: community
|
||||
|
||||
# The name of the secret containing the Neo4j password with the key NEO4J_PASSWORD
|
||||
passwordFromSecret: prowler-secret
|
||||
|
||||
# Disable lookups during helm template rendering (required for ArgoCD)
|
||||
disableLookups: true
|
||||
|
||||
volumes:
|
||||
data:
|
||||
mode: defaultStorageClass
|
||||
|
||||
services:
|
||||
neo4j:
|
||||
enabled: false
|
||||
|
||||
# Neo4j Configuration (yaml format)
|
||||
config:
|
||||
dbms_security_procedures_allowlist: "apoc.*"
|
||||
dbms_security_procedures_unrestricted: "apoc.*"
|
||||
|
||||
apoc_config:
|
||||
apoc.export.file.enabled: "true"
|
||||
apoc.import.file.enabled: "true"
|
||||
apoc.import.file.use_neo4j_config: "true"
|
||||
@@ -0,0 +1,41 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_cis
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
"""
|
||||
Generate CIS OCI Foundations Benchmark v3.1 compliance table.
|
||||
|
||||
Args:
|
||||
data: DataFrame containing compliance check results with columns:
|
||||
- REQUIREMENTS_ID: CIS requirement ID (e.g., "1.1", "2.1")
|
||||
- REQUIREMENTS_DESCRIPTION: Description of the requirement
|
||||
- REQUIREMENTS_ATTRIBUTES_SECTION: CIS section name
|
||||
- CHECKID: Prowler check identifier
|
||||
- STATUS: Check status (PASS/FAIL)
|
||||
- REGION: OCI region
|
||||
- ACCOUNTID: OCI tenancy OCID (renamed from TENANCYID)
|
||||
- RESOURCEID: Resource OCID or identifier
|
||||
|
||||
Returns:
|
||||
Section containers organized by CIS sections for dashboard display
|
||||
"""
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ID",
|
||||
"REQUIREMENTS_DESCRIPTION",
|
||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
].copy()
|
||||
|
||||
return get_section_containers_cis(
|
||||
aux, "REQUIREMENTS_ID", "REQUIREMENTS_ATTRIBUTES_SECTION"
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_kisa_ismsp
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
data["REQUIREMENTS_ID"] = (
|
||||
data["REQUIREMENTS_ID"] + " - " + data["REQUIREMENTS_DESCRIPTION"]
|
||||
)
|
||||
|
||||
data["REQUIREMENTS_ID"] = data["REQUIREMENTS_ID"].apply(
|
||||
lambda x: x[:150] + "..." if len(str(x)) > 150 else x
|
||||
)
|
||||
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ID",
|
||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
].copy()
|
||||
|
||||
return get_section_containers_kisa_ismsp(
|
||||
aux, "REQUIREMENTS_ATTRIBUTES_SECTION", "REQUIREMENTS_ID"
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_kisa_ismsp
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
data["REQUIREMENTS_ID"] = (
|
||||
data["REQUIREMENTS_ID"] + " - " + data["REQUIREMENTS_DESCRIPTION"]
|
||||
)
|
||||
|
||||
data["REQUIREMENTS_ID"] = data["REQUIREMENTS_ID"].apply(
|
||||
lambda x: x[:150] + "..." if len(str(x)) > 150 else x
|
||||
)
|
||||
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ID",
|
||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
].copy()
|
||||
|
||||
return get_section_containers_kisa_ismsp(
|
||||
aux, "REQUIREMENTS_ATTRIBUTES_SECTION", "REQUIREMENTS_ID"
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_kisa_ismsp
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
data["REQUIREMENTS_ID"] = (
|
||||
data["REQUIREMENTS_ID"] + " - " + data["REQUIREMENTS_DESCRIPTION"]
|
||||
)
|
||||
|
||||
data["REQUIREMENTS_ID"] = data["REQUIREMENTS_ID"].apply(
|
||||
lambda x: x[:150] + "..." if len(str(x)) > 150 else x
|
||||
)
|
||||
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ID",
|
||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
].copy()
|
||||
|
||||
return get_section_containers_kisa_ismsp(
|
||||
aux, "REQUIREMENTS_ATTRIBUTES_SECTION", "REQUIREMENTS_ID"
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_kisa_ismsp
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
data["REQUIREMENTS_ID"] = (
|
||||
data["REQUIREMENTS_ID"] + " - " + data["REQUIREMENTS_DESCRIPTION"]
|
||||
)
|
||||
|
||||
data["REQUIREMENTS_ID"] = data["REQUIREMENTS_ID"].apply(
|
||||
lambda x: x[:150] + "..." if len(str(x)) > 150 else x
|
||||
)
|
||||
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ID",
|
||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
].copy()
|
||||
|
||||
return get_section_containers_kisa_ismsp(
|
||||
aux, "REQUIREMENTS_ATTRIBUTES_SECTION", "REQUIREMENTS_ID"
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import warnings
|
||||
|
||||
from dashboard.common_methods import get_section_containers_kisa_ismsp
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
|
||||
def get_table(data):
|
||||
data["REQUIREMENTS_ID"] = (
|
||||
data["REQUIREMENTS_ID"] + " - " + data["REQUIREMENTS_DESCRIPTION"]
|
||||
)
|
||||
|
||||
data["REQUIREMENTS_ID"] = data["REQUIREMENTS_ID"].apply(
|
||||
lambda x: x[:150] + "..." if len(str(x)) > 150 else x
|
||||
)
|
||||
|
||||
aux = data[
|
||||
[
|
||||
"REQUIREMENTS_ID",
|
||||
"REQUIREMENTS_ATTRIBUTES_SECTION",
|
||||
"CHECKID",
|
||||
"STATUS",
|
||||
"REGION",
|
||||
"ACCOUNTID",
|
||||
"RESOURCEID",
|
||||
]
|
||||
].copy()
|
||||
|
||||
return get_section_containers_kisa_ismsp(
|
||||
aux, "REQUIREMENTS_ATTRIBUTES_SECTION", "REQUIREMENTS_ID"
|
||||
)
|
||||
@@ -284,6 +284,11 @@ def display_data(
|
||||
# Rename the column LOCATION to REGION for Alibaba Cloud
|
||||
if "alibabacloud" in analytics_input:
|
||||
data = data.rename(columns={"LOCATION": "REGION"})
|
||||
|
||||
# Rename the column TENANCYID to ACCOUNTID for Oracle Cloud
|
||||
if "oraclecloud" in analytics_input:
|
||||
data.rename(columns={"TENANCYID": "ACCOUNTID"}, inplace=True)
|
||||
|
||||
# Filter the chosen level of the CIS
|
||||
if is_level_1:
|
||||
data = data[data["REQUIREMENTS_ATTRIBUTES_PROFILE"].str.contains("Level 1")]
|
||||
|
||||
@@ -259,6 +259,8 @@ else:
|
||||
accounts.append(account + " - K8S")
|
||||
if "alibabacloud" in list(data[data["ACCOUNT_UID"] == account]["PROVIDER"]):
|
||||
accounts.append(account + " - ALIBABACLOUD")
|
||||
if "oraclecloud" in list(data[data["ACCOUNT_UID"] == account]["PROVIDER"]):
|
||||
accounts.append(account + " - OCI")
|
||||
|
||||
account_dropdown = create_account_dropdown(accounts)
|
||||
|
||||
@@ -306,6 +308,8 @@ else:
|
||||
services.append(service + " - M365")
|
||||
if "alibabacloud" in list(data[data["SERVICE_NAME"] == service]["PROVIDER"]):
|
||||
services.append(service + " - ALIBABACLOUD")
|
||||
if "oraclecloud" in list(data[data["SERVICE_NAME"] == service]["PROVIDER"]):
|
||||
services.append(service + " - OCI")
|
||||
|
||||
services = ["All"] + services
|
||||
services = [
|
||||
@@ -767,6 +771,8 @@ def filter_data(
|
||||
all_account_ids.append(account)
|
||||
if "alibabacloud" in list(data[data["ACCOUNT_UID"] == account]["PROVIDER"]):
|
||||
all_account_ids.append(account)
|
||||
if "oraclecloud" in list(data[data["ACCOUNT_UID"] == account]["PROVIDER"]):
|
||||
all_account_ids.append(account)
|
||||
|
||||
all_account_names = []
|
||||
if "ACCOUNT_NAME" in filtered_data.columns:
|
||||
@@ -793,6 +799,8 @@ def filter_data(
|
||||
data[data["ACCOUNT_UID"] == item]["PROVIDER"]
|
||||
):
|
||||
cloud_accounts_options.append(item + " - ALIBABACLOUD")
|
||||
if "oraclecloud" in list(data[data["ACCOUNT_UID"] == item]["PROVIDER"]):
|
||||
cloud_accounts_options.append(item + " - OCI")
|
||||
if "ACCOUNT_NAME" in filtered_data.columns:
|
||||
if "azure" in list(data[data["ACCOUNT_NAME"] == item]["PROVIDER"]):
|
||||
cloud_accounts_options.append(item + " - AZURE")
|
||||
@@ -925,6 +933,10 @@ def filter_data(
|
||||
filtered_data[filtered_data["SERVICE_NAME"] == item]["PROVIDER"]
|
||||
):
|
||||
service_filter_options.append(item + " - ALIBABACLOUD")
|
||||
if "oraclecloud" in list(
|
||||
filtered_data[filtered_data["SERVICE_NAME"] == item]["PROVIDER"]
|
||||
):
|
||||
service_filter_options.append(item + " - OCI")
|
||||
|
||||
# Filter Service
|
||||
if service_values == ["All"]:
|
||||
@@ -1124,6 +1136,7 @@ def filter_data(
|
||||
config={"displayModeBar": False},
|
||||
)
|
||||
table = dcc.Graph(figure=fig, config={"displayModeBar": False})
|
||||
table_row_options = []
|
||||
|
||||
else:
|
||||
# Status Pie Chart
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
@@ -36,6 +36,7 @@ The supported providers right now are:
|
||||
| [Cloudflare](/user-guide/providers/cloudflare/getting-started-cloudflare) | Official | Accounts | CLI |
|
||||
| [Infra as Code](/user-guide/providers/iac/getting-started-iac) | Official | Repositories | UI, API, CLI |
|
||||
| [MongoDB Atlas](/user-guide/providers/mongodbatlas/getting-started-mongodbatlas) | Official | Organizations | UI, API, CLI |
|
||||
| [OpenStack](/user-guide/providers/openstack/getting-started-openstack) | Official | Projects | CLI |
|
||||
| [LLM](/user-guide/providers/llm/getting-started-llm) | Official | Models | CLI |
|
||||
| **NHN** | Unofficial | Tenants | CLI |
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ GitHub has deprecated Personal Access Tokens (classic) in favor of fine-grained
|
||||
|
||||
4. **Configure Token Settings**
|
||||
- **Token name**: Give your token a descriptive name (e.g., "Prowler Security Scanner")
|
||||
- **Resource owner**: Select the account that owns the resources to scan — either a personal account or a specific organization
|
||||
- **Expiration**: Set an appropriate expiration date (recommended: 90 days or less)
|
||||
- **Repository access**: Choose "All repositories" or "Only select repositories" based on your needs
|
||||
|
||||
@@ -56,11 +57,11 @@ GitHub has deprecated Personal Access Tokens (classic) in favor of fine-grained
|
||||
- **Metadata**: Read-only access
|
||||
- **Pull requests**: Read-only access
|
||||
|
||||
- **Organization permissions:**
|
||||
- **Organization permissions** (available when an organization is selected as Resource Owner):
|
||||
- **Administration**: Read-only access
|
||||
- **Members**: Read-only access
|
||||
|
||||
- **Account permissions:**
|
||||
- **Account permissions** (available when a personal account is selected as Resource Owner):
|
||||
- **Email addresses**: Read-only access
|
||||
|
||||
6. **Copy and Store the Token**
|
||||
|
||||
@@ -54,7 +54,7 @@ title: 'Getting Started with GitHub'
|
||||
</Tabs>
|
||||
## Prowler CLI
|
||||
|
||||
### Automatic Login Method Detection
|
||||
### Authentication
|
||||
|
||||
If no login method is explicitly provided, Prowler will automatically attempt to authenticate using environment variables in the following order of precedence:
|
||||
|
||||
@@ -68,15 +68,15 @@ Ensure the corresponding environment variables are set up before running Prowler
|
||||
</Note>
|
||||
For more details on how to set up authentication with GitHub, see [Authentication > GitHub](/user-guide/providers/github/authentication).
|
||||
|
||||
### Personal Access Token (PAT)
|
||||
#### Personal Access Token (PAT)
|
||||
|
||||
Use this method by providing your personal access token directly.
|
||||
Use this method by providing a personal access token directly.
|
||||
|
||||
```console
|
||||
prowler github --personal-access-token pat
|
||||
```
|
||||
|
||||
### OAuth App Token
|
||||
#### OAuth App Token
|
||||
|
||||
Authenticate using an OAuth app token.
|
||||
|
||||
@@ -84,9 +84,62 @@ Authenticate using an OAuth app token.
|
||||
prowler github --oauth-app-token oauth_token
|
||||
```
|
||||
|
||||
### GitHub App Credentials
|
||||
#### GitHub App Credentials
|
||||
|
||||
Use GitHub App credentials by specifying the App ID and the private key path.
|
||||
|
||||
```console
|
||||
prowler github --github-app-id app_id --github-app-key-path app_key_path
|
||||
```
|
||||
|
||||
### Scan Scoping
|
||||
|
||||
By default, Prowler scans all repositories accessible to the authenticated user or organization. To limit the scan to specific repositories or organizations, use the following flags.
|
||||
|
||||
#### Scanning Specific Repositories
|
||||
|
||||
To restrict the scan to one or more repositories, use the `--repository` flag followed by the repository name(s) in `owner/repo-name` format:
|
||||
|
||||
```console
|
||||
prowler github --repository owner/repo-name
|
||||
```
|
||||
|
||||
To scan multiple repositories, specify them as space-separated arguments:
|
||||
|
||||
```console
|
||||
prowler github --repository owner/repo-name-1 owner/repo-name-2
|
||||
```
|
||||
|
||||
#### Scanning Specific Organizations
|
||||
|
||||
To restrict the scan to one or more organizations or user accounts, use the `--organization` flag:
|
||||
|
||||
```console
|
||||
prowler github --organization my-organization
|
||||
```
|
||||
|
||||
To scan multiple organizations, specify them as space-separated arguments:
|
||||
|
||||
```console
|
||||
prowler github --organization org-1 org-2
|
||||
```
|
||||
|
||||
#### Scanning Specific Repositories Within an Organization
|
||||
|
||||
To scan specific repositories within an organization, combine the `--organization` and `--repository` flags. The `--organization` flag qualifies unqualified repository names automatically:
|
||||
|
||||
```console
|
||||
prowler github --organization my-organization --repository my-repo
|
||||
```
|
||||
|
||||
This scans only `my-organization/my-repo`. Fully qualified repository names (`owner/repo-name`) are also supported alongside `--organization`:
|
||||
|
||||
```console
|
||||
prowler github --organization my-org --repository my-repo other-owner/other-repo
|
||||
```
|
||||
|
||||
In this case, `my-repo` is qualified as `my-org/my-repo`, while `other-owner/other-repo` is used as-is.
|
||||
|
||||
<Note>
|
||||
The `--repository` and `--organization` flags can be combined with any authentication method.
|
||||
</Note>
|
||||
|
||||
@@ -79,6 +79,31 @@ Choose a Method:
|
||||
|
||||
</Info>
|
||||
|
||||
**Configure Attribute Mapping in the IdP**
|
||||
|
||||
For Prowler App to correctly identify and provision users, configure the IdP to send the following attributes in the SAML assertion:
|
||||
|
||||
| Attribute Name | Description | Required |
|
||||
|----------------|---------------------------------------------------------------------------------------------------------|----------|
|
||||
| `firstName` | The user's first name. | Yes |
|
||||
| `lastName` | The user's last name. | Yes |
|
||||
| `userType` | The Prowler role to be assigned to the user (e.g., `admin`, `auditor`). If a role with that name already exists, it will be used; if it does not exist, a new role with that name will be created without permissions. If `userType` is not defined, the user is assigned the `no_permissions` role. Role permissions can be edited in the [RBAC Management tab](/user-guide/tutorials/prowler-app-rbac). | No |
|
||||
| `companyName` | The user's company name. This is automatically populated if the IdP sends an `organization` attribute. | No |
|
||||
|
||||
<Info>
|
||||
**IdP Attribute Mapping**
|
||||
|
||||
Note that the attribute name is just an example and may be different depending on the IdP. For instance, if the IdP provides a `division` attribute, it can be mapped to `userType`.
|
||||

|
||||
|
||||
</Info>
|
||||
<Warning>
|
||||
**Dynamic Updates**
|
||||
|
||||
Prowler App updates these attributes each time a user logs in. Any changes made in the Identity Provider (IdP) will be reflected when the user logs in again.
|
||||
|
||||
</Warning>
|
||||
|
||||
</Tab>
|
||||
<Tab title="Okta App Catalog">
|
||||
Instead of creating a custom SAML integration, Okta administrators can configure Prowler Cloud directly from Okta's application catalog.
|
||||
@@ -105,39 +130,48 @@ Choose a Method:
|
||||
|
||||
6. **Assign Users**: Navigate to the "Assignments" tab and assign the appropriate users or groups to the Prowler application by clicking "Assign" and selecting "Assign to People" or "Assign to Groups".
|
||||
|
||||

|
||||
|
||||
7. **Configure User Attributes in Okta**: Okta acts as the central source for user profile information. Prowler App maps the following Okta user profile attributes during each SAML login:
|
||||
|
||||
* **First name** (`firstName`): Maps to the user's first name in Prowler App.
|
||||
* **Last name** (`lastName`): Maps to the user's last name in Prowler App.
|
||||
|
||||

|
||||
|
||||
* **Organization** (`organization`): Maps to the company name displayed in Prowler App. This attribute is optional.
|
||||
* **User type** (`userType`): Determines the Prowler role assigned to the user. This attribute is **case-sensitive** and must match the exact name of an existing role in Prowler App.
|
||||
|
||||

|
||||
|
||||
To modify these values, edit the user's profile directly in the Okta admin console under the "Profile" tab. Changes are reflected in Prowler App the next time the user logs in via SAML.
|
||||
|
||||
<Warning>
|
||||
**User Type and Role Assignment**
|
||||
|
||||
The `userType` attribute controls which Prowler role is assigned to the user:
|
||||
|
||||
* If a role with the specified name already exists in Prowler App, the user automatically receives that role.
|
||||
* If the role does not exist, Prowler App creates a new role with that exact name but without any permissions, preventing the user from performing any actions.
|
||||
* If `userType` is not defined in the user's Okta profile, the user is assigned the `no_permissions` role.
|
||||
|
||||
In all cases where the resulting role has no permissions, a Prowler administrator (a user whose role includes the "Manage Account" permission) must configure the appropriate permissions through the [RBAC Management tab](/user-guide/tutorials/prowler-app-rbac).
|
||||
|
||||
This behavior is intentional: by defaulting to no permissions, Prowler App ensures that a misconfiguration in Okta cannot inadvertently grant elevated access.
|
||||
|
||||
**Example:** To assign the `IT` role to a user, set the `userType` value to `IT` in Okta. If a role named `IT` already exists in Prowler App, the user receives it automatically upon login. If it does not exist, Prowler App creates a new role called `IT` without permissions, and a Prowler administrator must configure the desired permissions for it.
|
||||
|
||||
</Warning>
|
||||
|
||||
With this step, the Okta app catalog configuration is complete. Users can now access Prowler Cloud using either [IdP-initiated](#idp-initiated-sso) or [SP-initiated SSO](#sp-initiated-sso) flows.
|
||||
|
||||
7. **Download Metadata XML**: Inside the "Sign On" section, go to the "Metadata URL" and download the metadata XML file.
|
||||
8. **Download Metadata XML**: Inside the "Sign On" section, go to the "Metadata URL" and download the metadata XML file.
|
||||
|
||||
Jump to [Step 5: Upload IdP Metadata to Prowler](#step-5:-upload-idp-metadata-to-prowler).
|
||||
Jump to [Step 4: Upload IdP Metadata to Prowler](#step-4:-upload-idp-metadata-to-prowler).
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
#### Step 4: Configure Attribute Mapping in the IdP
|
||||
|
||||
For Prowler App to correctly identify and provision users, configure the IdP to send the following attributes in the SAML assertion:
|
||||
|
||||
| Attribute Name | Description | Required |
|
||||
|----------------|---------------------------------------------------------------------------------------------------------|----------|
|
||||
| `firstName` | The user's first name. | Yes |
|
||||
| `lastName` | The user's last name. | Yes |
|
||||
| `userType` | The Prowler role to be assigned to the user (e.g., `admin`, `auditor`). If a role with that name already exists, it will be used; otherwise, a new role called `no_permissions` will be created with minimal permissions. Role permissions can be edited in the [RBAC Management tab](/user-guide/tutorials/prowler-app-rbac). | No |
|
||||
| `companyName` | The user's company name. This is automatically populated if the IdP sends an `organization` attribute. | No |
|
||||
|
||||
<Info>
|
||||
**IdP Attribute Mapping**
|
||||
|
||||
Note that the attribute name is just an example and may be different depending on the IdP. For instance, if the IdP provides a `division` attribute, it can be mapped to `userType`.
|
||||

|
||||
|
||||
</Info>
|
||||
<Warning>
|
||||
**Dynamic Updates**
|
||||
|
||||
Prowler App updates these attributes each time a user logs in. Any changes made in the Identity Provider (IdP) will be reflected when the user logs in again.
|
||||
|
||||
</Warning>
|
||||
#### Step 5: Upload IdP Metadata to Prowler
|
||||
#### Step 4: Upload IdP Metadata to Prowler
|
||||
|
||||
Once the IdP is configured, it provides a **metadata XML file**. This file contains the IdP's configuration information, such as its public key and login URL.
|
||||
|
||||
@@ -151,7 +185,7 @@ To complete the Prowler App configuration:
|
||||
|
||||

|
||||
|
||||
#### Step 6: Save and Verify Configuration
|
||||
#### Step 5: Save and Verify Configuration
|
||||
|
||||
Click the "Save" button to complete the setup. The "SAML Integration" card will now display an "Active" status, indicating the configuration is complete and enabled.
|
||||
|
||||
|
||||
+36
-1
@@ -2,6 +2,38 @@
|
||||
|
||||
All notable changes to the **Prowler SDK** are documented in this file.
|
||||
|
||||
## [5.19.0] (Prowler UNRELEASED)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- OpenStack provider `clouds_yaml_content` parameter for API integration [(#10003)](https://github.com/prowler-cloud/prowler/pull/10003)
|
||||
- `defender_safe_attachments_policy_enabled` check for M365 provider [(#9833)](https://github.com/prowler-cloud/prowler/pull/9833)
|
||||
- `defender_safelinks_policy_enabled` check for M365 provider [(#9832)](https://github.com/prowler-cloud/prowler/pull/9832)
|
||||
- AI Skills: Added a skill for creating new Attack Paths queries in openCypher, compatible with Neo4j and Neptune [(#9975)](https://github.com/prowler-cloud/prowler/pull/9975)
|
||||
- CSA CCM 4.0 for the AWS provider [(#10018)](https://github.com/prowler-cloud/prowler/pull/10018)
|
||||
- CSA CCM 4.0 for the GCP provider [(#10042)](https://github.com/prowler-cloud/prowler/pull/10042)
|
||||
- CSA CCM 4.0 for the Azure provider [(#10039)](https://github.com/prowler-cloud/prowler/pull/10039)
|
||||
- CSA CCM 4.0 for the Oracle Cloud provider [(#10057)](https://github.com/prowler-cloud/prowler/pull/10057)
|
||||
- OCI regions updater script and CI workflow [(#10020)](https://github.com/prowler-cloud/prowler/pull/10020)
|
||||
- `image` provider for container image scanning with Trivy integration [(#9984)](https://github.com/prowler-cloud/prowler/pull/9984)
|
||||
- CSA CCM 4.0 for the Alibaba Cloud provider [(#10061)](https://github.com/prowler-cloud/prowler/pull/10061)
|
||||
- `entra_policy_unknown_unsupported_device_platforms_blocked` check for M365 provider [(#10063)](https://github.com/prowler-cloud/prowler/pull/10063)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
- Update Azure Monitor service metadata to new format [(#9622)](https://github.com/prowler-cloud/prowler/pull/9622)
|
||||
- Parallelize Cloudflare zone API calls with threading to improve scan performance [(#9982)](https://github.com/prowler-cloud/prowler/pull/9982)
|
||||
- Update GCP API Keys service metadata to new format [(#9637)](https://github.com/prowler-cloud/prowler/pull/9637)
|
||||
|
||||
## [5.18.2] (Prowler UNRELEASED)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
- `--repository` and `--organization` flags combined interaction in GitHub provider, qualifying unqualified repository names with organization [(#10001)](https://github.com/prowler-cloud/prowler/pull/10001)
|
||||
- HPACK library logging tokens in debug mode for Azure, M365, and Cloudflare providers [(#10010)](https://github.com/prowler-cloud/prowler/pull/10010)
|
||||
|
||||
---
|
||||
|
||||
## [5.18.0] (Prowler v5.18.0)
|
||||
|
||||
### 🚀 Added
|
||||
@@ -9,6 +41,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `defender_zap_for_teams_enabled` check for M365 provider [(#9838)](https://github.com/prowler-cloud/prowler/pull/9838)
|
||||
- `compute_instance_suspended_without_persistent_disks` check for GCP provider [(#9747)](https://github.com/prowler-cloud/prowler/pull/9747)
|
||||
- `codebuild_project_webhook_filters_use_anchored_patterns` check for AWS provider to detect CodeBreach vulnerability [(#9840)](https://github.com/prowler-cloud/prowler/pull/9840)
|
||||
- `defender_atp_safe_attachments_policy_enabled` check for M365 provider [(#9837)](https://github.com/prowler-cloud/prowler/pull/9837)
|
||||
- `exchange_shared_mailbox_sign_in_disabled` check for M365 provider [(#9828)](https://github.com/prowler-cloud/prowler/pull/9828)
|
||||
- CloudTrail Timeline abstraction for querying resource modification history [(#9101)](https://github.com/prowler-cloud/prowler/pull/9101)
|
||||
- Cloudflare `--account-id` filter argument [(#9894)](https://github.com/prowler-cloud/prowler/pull/9894)
|
||||
@@ -17,6 +50,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `OpenStack` documentation for the support in the CLI [(#9848)](https://github.com/prowler-cloud/prowler/pull/9848)
|
||||
- Add HIPAA compliance framework for the Azure provider [(#9957)](https://github.com/prowler-cloud/prowler/pull/9957)
|
||||
- Cloudflare provider credentials as constructor parameters (`api_token`, `api_key`, `api_email`) [(#9907)](https://github.com/prowler-cloud/prowler/pull/9907)
|
||||
- CIS 3.1 for the Oracle Cloud provider [(#9971)](https://github.com/prowler-cloud/prowler/pull/9971)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
@@ -36,7 +70,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- Update Azure Network service metadata to new format [(#9624)](https://github.com/prowler-cloud/prowler/pull/9624)
|
||||
- Update Azure Storage service metadata to new format [(#9628)](https://github.com/prowler-cloud/prowler/pull/9628)
|
||||
|
||||
### 🐛 Fixed
|
||||
### 🐞 Fixed
|
||||
|
||||
- Duplicated findings in `entra_user_with_vm_access_has_mfa` check when user has multiple VM access roles [(#9914)](https://github.com/prowler-cloud/prowler/pull/9914)
|
||||
- Jira integration failing with `INVALID_INPUT` error when sending findings with long resource UIDs exceeding 255-character summary limit [(#9926)](https://github.com/prowler-cloud/prowler/pull/9926)
|
||||
@@ -104,6 +138,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- Update Azure AI Search service metadata to new format [(#9087)](https://github.com/prowler-cloud/prowler/pull/9087)
|
||||
- Update Azure AKS service metadata to new format [(#9611)](https://github.com/prowler-cloud/prowler/pull/9611)
|
||||
- Update Azure API Management service metadata to new format [(#9612)](https://github.com/prowler-cloud/prowler/pull/9612)
|
||||
- Enhance AWS IAM privilege escalation detection with patterns from pathfinding.cloud library [(#9922)](https://github.com/prowler-cloud/prowler/pull/9922)
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
+81
-7
@@ -8,6 +8,7 @@ from colorama import Fore, Style
|
||||
from colorama import init as colorama_init
|
||||
|
||||
from prowler.config.config import (
|
||||
EXTERNAL_TOOL_PROVIDERS,
|
||||
csv_file_suffix,
|
||||
get_available_compliance_frameworks,
|
||||
html_file_suffix,
|
||||
@@ -65,6 +66,11 @@ from prowler.lib.outputs.compliance.cis.cis_kubernetes import KubernetesCIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_m365 import M365CIS
|
||||
from prowler.lib.outputs.compliance.cis.cis_oraclecloud import OracleCloudCIS
|
||||
from prowler.lib.outputs.compliance.compliance import display_compliance_table
|
||||
from prowler.lib.outputs.compliance.csa.csa_alibabacloud import AlibabaCloudCSA
|
||||
from prowler.lib.outputs.compliance.csa.csa_aws import AWSCSA
|
||||
from prowler.lib.outputs.compliance.csa.csa_azure import AzureCSA
|
||||
from prowler.lib.outputs.compliance.csa.csa_gcp import GCPCSA
|
||||
from prowler.lib.outputs.compliance.csa.csa_oraclecloud import OracleCloudCSA
|
||||
from prowler.lib.outputs.compliance.ens.ens_aws import AWSENS
|
||||
from prowler.lib.outputs.compliance.ens.ens_azure import AzureENS
|
||||
from prowler.lib.outputs.compliance.ens.ens_gcp import GCPENS
|
||||
@@ -119,6 +125,8 @@ from prowler.providers.common.quick_inventory import run_provider_quick_inventor
|
||||
from prowler.providers.gcp.models import GCPOutputOptions
|
||||
from prowler.providers.github.models import GithubOutputOptions
|
||||
from prowler.providers.iac.models import IACOutputOptions
|
||||
from prowler.providers.image.exceptions.exceptions import ImageBaseException
|
||||
from prowler.providers.image.models import ImageOutputOptions
|
||||
from prowler.providers.kubernetes.models import KubernetesOutputOptions
|
||||
from prowler.providers.llm.models import LLMOutputOptions
|
||||
from prowler.providers.m365.models import M365OutputOptions
|
||||
@@ -206,8 +214,8 @@ def prowler():
|
||||
# Load compliance frameworks
|
||||
logger.debug("Loading compliance frameworks from .json files")
|
||||
|
||||
# Skip compliance frameworks for IAC and LLM providers
|
||||
if provider != "iac" and provider != "llm":
|
||||
# Skip compliance frameworks for external-tool providers
|
||||
if provider not in EXTERNAL_TOOL_PROVIDERS:
|
||||
bulk_compliance_frameworks = Compliance.get_bulk(provider)
|
||||
# Complete checks metadata with the compliance framework specification
|
||||
bulk_checks_metadata = update_checks_metadata_with_compliance(
|
||||
@@ -264,8 +272,8 @@ def prowler():
|
||||
if not args.only_logs:
|
||||
global_provider.print_credentials()
|
||||
|
||||
# Skip service and check loading for IAC and LLM providers
|
||||
if provider != "iac" and provider != "llm":
|
||||
# Skip service and check loading for external-tool providers
|
||||
if provider not in EXTERNAL_TOOL_PROVIDERS:
|
||||
# Import custom checks from folder
|
||||
if checks_folder:
|
||||
custom_checks = parse_checks_from_folder(global_provider, checks_folder)
|
||||
@@ -352,6 +360,8 @@ def prowler():
|
||||
)
|
||||
elif provider == "iac":
|
||||
output_options = IACOutputOptions(args, bulk_checks_metadata)
|
||||
elif provider == "image":
|
||||
output_options = ImageOutputOptions(args, bulk_checks_metadata)
|
||||
elif provider == "llm":
|
||||
output_options = LLMOutputOptions(args, bulk_checks_metadata)
|
||||
elif provider == "oraclecloud":
|
||||
@@ -375,8 +385,8 @@ def prowler():
|
||||
# Execute checks
|
||||
findings = []
|
||||
|
||||
if provider == "iac" or provider == "llm":
|
||||
# For IAC and LLM providers, run the scan directly
|
||||
if provider in EXTERNAL_TOOL_PROVIDERS:
|
||||
# For external-tool providers, run the scan directly
|
||||
if provider == "llm":
|
||||
|
||||
def streaming_callback(findings_batch):
|
||||
@@ -386,7 +396,11 @@ def prowler():
|
||||
findings = global_provider.run_scan(streaming_callback=streaming_callback)
|
||||
else:
|
||||
# Original behavior for IAC or non-verbose LLM
|
||||
findings = global_provider.run()
|
||||
try:
|
||||
findings = global_provider.run()
|
||||
except ImageBaseException as error:
|
||||
logger.critical(f"{error}")
|
||||
sys.exit(1)
|
||||
# Note: IaC doesn't support granular progress tracking since Trivy runs as a black box
|
||||
# and returns all findings at once. Progress tracking would just be 0% → 100%.
|
||||
|
||||
@@ -626,6 +640,18 @@ def prowler():
|
||||
)
|
||||
generated_outputs["compliance"].append(c5)
|
||||
c5.batch_write_data_to_file()
|
||||
elif compliance_name == "csa_ccm_4.0_aws":
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
csa_ccm_4_0_aws = AWSCSA(
|
||||
findings=finding_outputs,
|
||||
compliance=bulk_compliance_frameworks[compliance_name],
|
||||
file_path=filename,
|
||||
)
|
||||
generated_outputs["compliance"].append(csa_ccm_4_0_aws)
|
||||
csa_ccm_4_0_aws.batch_write_data_to_file()
|
||||
else:
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
@@ -729,6 +755,18 @@ def prowler():
|
||||
)
|
||||
generated_outputs["compliance"].append(c5_azure)
|
||||
c5_azure.batch_write_data_to_file()
|
||||
elif compliance_name == "csa_ccm_4.0_azure":
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
csa_ccm_4_0_azure = AzureCSA(
|
||||
findings=finding_outputs,
|
||||
compliance=bulk_compliance_frameworks[compliance_name],
|
||||
file_path=filename,
|
||||
)
|
||||
generated_outputs["compliance"].append(csa_ccm_4_0_azure)
|
||||
csa_ccm_4_0_azure.batch_write_data_to_file()
|
||||
else:
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
@@ -832,6 +870,18 @@ def prowler():
|
||||
)
|
||||
generated_outputs["compliance"].append(c5_gcp)
|
||||
c5_gcp.batch_write_data_to_file()
|
||||
elif compliance_name == "csa_ccm_4.0_gcp":
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
csa_ccm_4_0_gcp = GCPCSA(
|
||||
findings=finding_outputs,
|
||||
compliance=bulk_compliance_frameworks[compliance_name],
|
||||
file_path=filename,
|
||||
)
|
||||
generated_outputs["compliance"].append(csa_ccm_4_0_gcp)
|
||||
csa_ccm_4_0_gcp.batch_write_data_to_file()
|
||||
else:
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
@@ -1024,6 +1074,18 @@ def prowler():
|
||||
)
|
||||
generated_outputs["compliance"].append(cis)
|
||||
cis.batch_write_data_to_file()
|
||||
elif compliance_name == "csa_ccm_4.0_oraclecloud":
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
csa_ccm_4_0_oraclecloud = OracleCloudCSA(
|
||||
findings=finding_outputs,
|
||||
compliance=bulk_compliance_frameworks[compliance_name],
|
||||
file_path=filename,
|
||||
)
|
||||
generated_outputs["compliance"].append(csa_ccm_4_0_oraclecloud)
|
||||
csa_ccm_4_0_oraclecloud.batch_write_data_to_file()
|
||||
else:
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
@@ -1052,6 +1114,18 @@ def prowler():
|
||||
)
|
||||
generated_outputs["compliance"].append(cis)
|
||||
cis.batch_write_data_to_file()
|
||||
elif compliance_name == "csa_ccm_4.0_alibabacloud":
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
f"{output_options.output_filename}_{compliance_name}.csv"
|
||||
)
|
||||
csa_ccm_4_0_alibabacloud = AlibabaCloudCSA(
|
||||
findings=finding_outputs,
|
||||
compliance=bulk_compliance_frameworks[compliance_name],
|
||||
file_path=filename,
|
||||
)
|
||||
generated_outputs["compliance"].append(csa_ccm_4_0_alibabacloud)
|
||||
csa_ccm_4_0_alibabacloud.batch_write_data_to_file()
|
||||
elif compliance_name == "prowler_threatscore_alibabacloud":
|
||||
filename = (
|
||||
f"{output_options.output_directory}/compliance/"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -318,7 +318,9 @@
|
||||
{
|
||||
"Id": "2.1.1",
|
||||
"Description": "Enabling Safe Links policy for Office applications allows URL's that exist inside of Office documents and email applications opened by Office, Office Online and Office mobile to be processed against Defender for Office time-of-click verification and rewritten if required.**Note:** E5 Licensing includes a number of Built-in Protection policies. When auditing policies note which policy you are viewing, and keep in mind CIS recommendations often extend the Default or Build-in Policies provided by MS. In order to **Pass** the highest priority policy must match all settings recommended.",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"defender_safelinks_policy_enabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2 Microsoft 365 Defender",
|
||||
@@ -385,7 +387,9 @@
|
||||
{
|
||||
"Id": "2.1.4",
|
||||
"Description": "The Safe Attachments policy helps protect users from malware in email attachments by scanning attachments for viruses, malware, and other malicious content. When an email attachment is received by a user, Safe Attachments will scan the attachment in a secure environment and provide a verdict on whether the attachment is safe or not.",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"defender_safe_attachments_policy_enabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2 Microsoft 365 Defender",
|
||||
@@ -406,7 +410,9 @@
|
||||
{
|
||||
"Id": "2.1.5",
|
||||
"Description": "Safe Attachments for SharePoint, OneDrive, and Microsoft Teams scans these services for malicious files.",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"defender_atp_safe_attachments_and_docs_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2 Microsoft 365 Defender",
|
||||
|
||||
@@ -339,7 +339,9 @@
|
||||
{
|
||||
"Id": "2.1.1",
|
||||
"Description": "Safe Links for Office Applications is a feature in Microsoft 365 that provides URL scanning and rewriting of inbound email messages in mail flow, and time-of-click verification of URLs and links in email messages, other Microsoft 365 applications such as Teams, and other locations such as SharePoint Online. It can help protect organizations from malicious links used in phishing and other attacks.",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"defender_safelinks_policy_enabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2 Microsoft 365 Defender",
|
||||
@@ -404,7 +406,9 @@
|
||||
{
|
||||
"Id": "2.1.4",
|
||||
"Description": "The Safe Attachments policy helps protect users from malware in email attachments by scanning attachments for viruses, malware, and other malicious content. When an email attachment is received by a user, Safe Attachments will scan the attachment in a secure environment and provide a verdict on whether the attachment is safe or not.",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"defender_safe_attachments_policy_enabled"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2 Microsoft 365 Defender",
|
||||
@@ -425,7 +429,9 @@
|
||||
{
|
||||
"Id": "2.1.5",
|
||||
"Description": "Safe Attachments for SharePoint, OneDrive, and Microsoft Teams scans these services for malicious files.",
|
||||
"Checks": [],
|
||||
"Checks": [
|
||||
"defender_atp_safe_attachments_and_docs_configured"
|
||||
],
|
||||
"Attributes": [
|
||||
{
|
||||
"Section": "2 Microsoft 365 Defender",
|
||||
|
||||
@@ -115,6 +115,7 @@
|
||||
"defender_antiphishing_policy_configured",
|
||||
"defender_antispam_outbound_policy_configured",
|
||||
"defender_malware_policy_notifications_internal_users_malware_enabled",
|
||||
"defender_safelinks_policy_enabled",
|
||||
"defender_zap_for_teams_enabled",
|
||||
"entra_admin_users_phishing_resistant_mfa_enabled",
|
||||
"entra_identity_protection_sign_in_risk_enabled",
|
||||
@@ -198,7 +199,8 @@
|
||||
"admincenter_users_admins_reduced_license_footprint",
|
||||
"entra_admin_portals_access_restriction",
|
||||
"entra_admin_users_phishing_resistant_mfa_enabled",
|
||||
"entra_policy_guest_users_access_restrictions"
|
||||
"entra_policy_guest_users_access_restrictions",
|
||||
"entra_policy_unknown_unsupported_device_platforms_blocked"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -602,10 +604,11 @@
|
||||
],
|
||||
"Checks": [
|
||||
"entra_managed_device_required_for_authentication",
|
||||
"entra_users_mfa_enabled",
|
||||
"entra_managed_device_required_for_mfa_registration",
|
||||
"entra_admin_users_phishing_resistant_mfa_enabled",
|
||||
"entra_users_mfa_capable"
|
||||
"entra_policy_unknown_unsupported_device_platforms_blocked",
|
||||
"entra_users_mfa_capable",
|
||||
"entra_users_mfa_enabled",
|
||||
"entra_admin_users_phishing_resistant_mfa_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -669,11 +672,12 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"entra_admin_users_sign_in_frequency_enabled",
|
||||
"entra_admin_users_mfa_enabled",
|
||||
"entra_admin_users_sign_in_frequency_enabled",
|
||||
"entra_identity_protection_sign_in_risk_enabled",
|
||||
"entra_managed_device_required_for_authentication",
|
||||
"entra_users_mfa_enabled",
|
||||
"entra_identity_protection_sign_in_risk_enabled"
|
||||
"entra_policy_unknown_unsupported_device_platforms_blocked",
|
||||
"entra_users_mfa_enabled"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -692,6 +696,8 @@
|
||||
"defender_malware_policy_common_attachments_filter_enabled",
|
||||
"defender_malware_policy_comprehensive_attachments_filter_applied",
|
||||
"defender_malware_policy_notifications_internal_users_malware_enabled",
|
||||
"defender_safe_attachments_policy_enabled",
|
||||
"defender_safelinks_policy_enabled",
|
||||
"defender_zap_for_teams_enabled",
|
||||
"teams_external_domains_restricted",
|
||||
"teams_external_users_cannot_start_conversations"
|
||||
@@ -729,6 +735,7 @@
|
||||
],
|
||||
"Checks": [
|
||||
"defender_antiphishing_policy_configured",
|
||||
"defender_safelinks_policy_enabled",
|
||||
"entra_admin_users_phishing_resistant_mfa_enabled"
|
||||
]
|
||||
},
|
||||
@@ -833,10 +840,11 @@
|
||||
}
|
||||
],
|
||||
"Checks": [
|
||||
"teams_external_domains_restricted",
|
||||
"teams_external_users_cannot_start_conversations",
|
||||
"defender_safelinks_policy_enabled",
|
||||
"sharepoint_external_sharing_managed",
|
||||
"sharepoint_external_sharing_restricted",
|
||||
"sharepoint_external_sharing_managed"
|
||||
"teams_external_domains_restricted",
|
||||
"teams_external_users_cannot_start_conversations"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -38,7 +38,7 @@ class _MutableTimestamp:
|
||||
|
||||
timestamp = _MutableTimestamp(datetime.today())
|
||||
timestamp_utc = _MutableTimestamp(datetime.now(timezone.utc))
|
||||
prowler_version = "5.18.1"
|
||||
prowler_version = "5.19.0"
|
||||
html_logo_url = "https://github.com/prowler-cloud/prowler/"
|
||||
square_logo_img = "https://raw.githubusercontent.com/prowler-cloud/prowler/dc7d2d5aeb92fdf12e8604f42ef6472cd3e8e889/docs/img/prowler-logo-black.png"
|
||||
aws_logo = "https://user-images.githubusercontent.com/38561120/235953920-3e3fba08-0795-41dc-b480-9bea57db9f2e.png"
|
||||
@@ -63,8 +63,13 @@ class Provider(str, Enum):
|
||||
ORACLECLOUD = "oraclecloud"
|
||||
ALIBABACLOUD = "alibabacloud"
|
||||
OPENSTACK = "openstack"
|
||||
IMAGE = "image"
|
||||
|
||||
|
||||
# Providers that delegate scanning to an external tool (e.g. Trivy, promptfoo)
|
||||
# and bypass standard check/service loading.
|
||||
EXTERNAL_TOOL_PROVIDERS = frozenset({"iac", "llm", "image"})
|
||||
|
||||
# Compliance
|
||||
actual_directory = pathlib.Path(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
@@ -75,7 +80,10 @@ def get_available_compliance_frameworks(provider=None):
|
||||
if provider:
|
||||
providers = [provider]
|
||||
for provider in providers:
|
||||
with os.scandir(f"{actual_directory}/../compliance/{provider}") as files:
|
||||
compliance_dir = f"{actual_directory}/../compliance/{provider}"
|
||||
if not os.path.isdir(compliance_dir):
|
||||
continue
|
||||
with os.scandir(compliance_dir) as files:
|
||||
for file in files:
|
||||
if file.is_file() and file.name.endswith(".json"):
|
||||
available_compliance_frameworks.append(
|
||||
|
||||
@@ -226,6 +226,18 @@ class C5Germany_Requirement_Attribute(BaseModel):
|
||||
ComplementaryCriteria: str
|
||||
|
||||
|
||||
# CSA CCM v4 Requirement Attribute
|
||||
class CSA_CCM_Requirement_Attribute(BaseModel):
|
||||
"""CSA Cloud Controls Matrix (CCM) v4 Requirement Attribute"""
|
||||
|
||||
Section: str
|
||||
CCMLite: str
|
||||
IaaS: str
|
||||
PaaS: str
|
||||
SaaS: str
|
||||
ScopeApplicability: list[dict]
|
||||
|
||||
|
||||
# Base Compliance Model
|
||||
# TODO: move this to compliance folder
|
||||
class Compliance_Requirement(BaseModel):
|
||||
@@ -244,6 +256,7 @@ class Compliance_Requirement(BaseModel):
|
||||
Prowler_ThreatScore_Requirement_Attribute,
|
||||
CCC_Requirement_Attribute,
|
||||
C5Germany_Requirement_Attribute,
|
||||
CSA_CCM_Requirement_Attribute,
|
||||
# Generic_Compliance_Requirement_Attribute must be the last one since it is the fallback for generic compliance framework
|
||||
Generic_Compliance_Requirement_Attribute,
|
||||
]
|
||||
|
||||
+46
-11
@@ -10,7 +10,7 @@ from typing import Any, Dict, Optional, Set
|
||||
from pydantic.v1 import BaseModel, Field, ValidationError, validator
|
||||
from pydantic.v1.error_wrappers import ErrorWrapper
|
||||
|
||||
from prowler.config.config import Provider
|
||||
from prowler.config.config import EXTERNAL_TOOL_PROVIDERS, Provider
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.check.utils import recover_checks_from_provider
|
||||
from prowler.lib.logger import logger
|
||||
@@ -159,11 +159,7 @@ class CheckMetadata(BaseModel):
|
||||
raise ValueError("ServiceName must be a non-empty string")
|
||||
|
||||
check_id = values.get("CheckID")
|
||||
if (
|
||||
check_id
|
||||
and values.get("Provider") != "iac"
|
||||
and values.get("Provider") != "llm"
|
||||
):
|
||||
if check_id and values.get("Provider") not in EXTERNAL_TOOL_PROVIDERS:
|
||||
service_from_check_id = check_id.split("_")[0]
|
||||
if service_name != service_from_check_id:
|
||||
raise ValueError(
|
||||
@@ -179,11 +175,7 @@ class CheckMetadata(BaseModel):
|
||||
if not check_id:
|
||||
raise ValueError("CheckID must be a non-empty string")
|
||||
|
||||
if (
|
||||
check_id
|
||||
and values.get("Provider") != "iac"
|
||||
and values.get("Provider") != "llm"
|
||||
):
|
||||
if check_id and values.get("Provider") not in EXTERNAL_TOOL_PROVIDERS:
|
||||
if "-" in check_id:
|
||||
raise ValueError(
|
||||
f"CheckID {check_id} contains a hyphen, which is not allowed"
|
||||
@@ -865,6 +857,49 @@ class CheckReportIAC(Check_Report):
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class CheckReportImage(Check_Report):
|
||||
"""Contains the Container Image Check's finding information using Trivy."""
|
||||
|
||||
resource_name: str
|
||||
resource_id: str
|
||||
image_digest: str
|
||||
package_name: str
|
||||
installed_version: str
|
||||
fixed_version: str
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
metadata: Optional[dict] = None,
|
||||
finding: Optional[dict] = None,
|
||||
image_name: str = "",
|
||||
) -> None:
|
||||
"""
|
||||
Initialize the Container Image Check's finding information from a Trivy vulnerability/secret dict.
|
||||
|
||||
Args:
|
||||
metadata (Dict): Check metadata.
|
||||
finding (dict): A single vulnerability/secret result from Trivy's JSON output.
|
||||
image_name (str): The container image name being scanned.
|
||||
"""
|
||||
if metadata is None:
|
||||
metadata = {}
|
||||
if finding is None:
|
||||
finding = {}
|
||||
super().__init__(metadata, finding)
|
||||
|
||||
self.resource_name = image_name
|
||||
self.resource_id = (
|
||||
finding.get("VulnerabilityID", "")
|
||||
or finding.get("RuleID", "")
|
||||
or finding.get("ID", "")
|
||||
)
|
||||
self.image_digest = finding.get("PkgID", "")
|
||||
self.package_name = finding.get("PkgName", "")
|
||||
self.installed_version = finding.get("InstalledVersion", "")
|
||||
self.fixed_version = finding.get("FixedVersion", "")
|
||||
|
||||
|
||||
@dataclass
|
||||
class CheckReportLLM(Check_Report):
|
||||
"""Contains the LLM Check's finding information."""
|
||||
|
||||
@@ -14,8 +14,8 @@ def recover_checks_from_provider(
|
||||
Returns a list of tuples with the following format (check_name, check_path)
|
||||
"""
|
||||
try:
|
||||
# Bypass check loading for IAC provider since it uses Trivy directly
|
||||
if provider == "iac" or provider == "llm":
|
||||
# Bypass check loading for IAC, LLM, and Image providers since they use external tools directly
|
||||
if provider in ("iac", "llm", "image"):
|
||||
return []
|
||||
|
||||
checks = []
|
||||
|
||||
@@ -27,10 +27,10 @@ class ProwlerArgumentParser:
|
||||
self.parser = argparse.ArgumentParser(
|
||||
prog="prowler",
|
||||
formatter_class=RawTextHelpFormatter,
|
||||
usage="prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,github,nhn,mongodbatlas,oraclecloud,alibabacloud,cloudflare,openstack,dashboard,iac} ...",
|
||||
usage="prowler [-h] [--version] {aws,azure,gcp,kubernetes,m365,github,nhn,mongodbatlas,oraclecloud,alibabacloud,cloudflare,openstack,dashboard,iac,image} ...",
|
||||
epilog="""
|
||||
Available Cloud Providers:
|
||||
{aws,azure,gcp,kubernetes,m365,github,iac,llm,nhn,mongodbatlas,oraclecloud,alibabacloud,cloudflare,openstack}
|
||||
{aws,azure,gcp,kubernetes,m365,github,iac,llm,image,nhn,mongodbatlas,oraclecloud,alibabacloud,cloudflare,openstack}
|
||||
aws AWS Provider
|
||||
azure Azure Provider
|
||||
gcp GCP Provider
|
||||
@@ -43,6 +43,7 @@ Available Cloud Providers:
|
||||
alibabacloud Alibaba Cloud Provider
|
||||
iac IaC Provider (Beta)
|
||||
llm LLM Provider (Beta)
|
||||
image Container Image Provider
|
||||
nhn NHN Provider (Unofficial)
|
||||
mongodbatlas MongoDB Atlas Provider (Beta)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from prowler.lib.check.models import Check_Report
|
||||
from prowler.lib.logger import logger
|
||||
from prowler.lib.outputs.compliance.c5.c5 import get_c5_table
|
||||
from prowler.lib.outputs.compliance.cis.cis import get_cis_table
|
||||
from prowler.lib.outputs.compliance.csa.csa import get_csa_table
|
||||
from prowler.lib.outputs.compliance.ens.ens import get_ens_table
|
||||
from prowler.lib.outputs.compliance.generic.generic_table import (
|
||||
get_generic_compliance_table,
|
||||
@@ -85,6 +86,15 @@ def display_compliance_table(
|
||||
output_directory,
|
||||
compliance_overview,
|
||||
)
|
||||
elif "csa_ccm_" in compliance_framework:
|
||||
get_csa_table(
|
||||
findings,
|
||||
bulk_checks_metadata,
|
||||
compliance_framework,
|
||||
output_filename,
|
||||
output_directory,
|
||||
compliance_overview,
|
||||
)
|
||||
elif "c5_" in compliance_framework:
|
||||
get_c5_table(
|
||||
findings,
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
from colorama import Fore, Style
|
||||
from tabulate import tabulate
|
||||
|
||||
from prowler.config.config import orange_color
|
||||
|
||||
|
||||
def get_csa_table(
|
||||
findings: list,
|
||||
bulk_checks_metadata: dict,
|
||||
compliance_framework: str,
|
||||
output_filename: str,
|
||||
output_directory: str,
|
||||
compliance_overview: bool,
|
||||
):
|
||||
section_table = {
|
||||
"Provider": [],
|
||||
"Section": [],
|
||||
"Status": [],
|
||||
"Muted": [],
|
||||
}
|
||||
pass_count = []
|
||||
fail_count = []
|
||||
muted_count = []
|
||||
sections = {}
|
||||
for index, finding in enumerate(findings):
|
||||
check = bulk_checks_metadata[finding.check_metadata.CheckID]
|
||||
check_compliances = check.Compliance
|
||||
for compliance in check_compliances:
|
||||
if (
|
||||
compliance.Framework == "CSA-CCM"
|
||||
and compliance.Version in compliance_framework
|
||||
):
|
||||
for requirement in compliance.Requirements:
|
||||
for attribute in requirement.Attributes:
|
||||
section = attribute.Section
|
||||
|
||||
if section not in sections:
|
||||
sections[section] = {"FAIL": 0, "PASS": 0, "Muted": 0}
|
||||
|
||||
if finding.muted:
|
||||
if index not in muted_count:
|
||||
muted_count.append(index)
|
||||
sections[section]["Muted"] += 1
|
||||
else:
|
||||
if finding.status == "FAIL" and index not in fail_count:
|
||||
fail_count.append(index)
|
||||
sections[section]["FAIL"] += 1
|
||||
elif finding.status == "PASS" and index not in pass_count:
|
||||
pass_count.append(index)
|
||||
sections[section]["PASS"] += 1
|
||||
|
||||
sections = dict(sorted(sections.items()))
|
||||
for section in sections:
|
||||
section_table["Provider"].append(compliance.Provider)
|
||||
section_table["Section"].append(section)
|
||||
if sections[section]["FAIL"] > 0:
|
||||
section_table["Status"].append(
|
||||
f"{Fore.RED}FAIL({sections[section]['FAIL']}){Style.RESET_ALL}"
|
||||
)
|
||||
else:
|
||||
if sections[section]["PASS"] > 0:
|
||||
section_table["Status"].append(
|
||||
f"{Fore.GREEN}PASS({sections[section]['PASS']}){Style.RESET_ALL}"
|
||||
)
|
||||
else:
|
||||
section_table["Status"].append(f"{Fore.GREEN}PASS{Style.RESET_ALL}")
|
||||
section_table["Muted"].append(
|
||||
f"{orange_color}{sections[section]['Muted']}{Style.RESET_ALL}"
|
||||
)
|
||||
|
||||
if (
|
||||
len(fail_count) + len(pass_count) + len(muted_count) > 1
|
||||
): # If there are no resources, don't print the compliance table
|
||||
print(
|
||||
f"\nCompliance Status of {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Framework:"
|
||||
)
|
||||
total_findings_count = len(fail_count) + len(pass_count) + len(muted_count)
|
||||
overview_table = [
|
||||
[
|
||||
f"{Fore.RED}{round(len(fail_count) / total_findings_count * 100, 2)}% ({len(fail_count)}) FAIL{Style.RESET_ALL}",
|
||||
f"{Fore.GREEN}{round(len(pass_count) / total_findings_count * 100, 2)}% ({len(pass_count)}) PASS{Style.RESET_ALL}",
|
||||
f"{orange_color}{round(len(muted_count) / total_findings_count * 100, 2)}% ({len(muted_count)}) MUTED{Style.RESET_ALL}",
|
||||
]
|
||||
]
|
||||
print(tabulate(overview_table, tablefmt="rounded_grid"))
|
||||
if not compliance_overview:
|
||||
if len(fail_count) > 0 and len(section_table["Section"]) > 0:
|
||||
print(
|
||||
f"\nFramework {Fore.YELLOW}{compliance_framework.upper()}{Style.RESET_ALL} Results:"
|
||||
)
|
||||
print(
|
||||
tabulate(
|
||||
section_table,
|
||||
tablefmt="rounded_grid",
|
||||
headers="keys",
|
||||
)
|
||||
)
|
||||
print(f"\nDetailed results of {compliance_framework.upper()} are in:")
|
||||
print(
|
||||
f" - CSV: {output_directory}/compliance/{output_filename}_{compliance_framework}.csv\n"
|
||||
)
|
||||
@@ -0,0 +1,96 @@
|
||||
from prowler.config.config import timestamp
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.outputs.compliance.compliance_output import ComplianceOutput
|
||||
from prowler.lib.outputs.compliance.csa.models import AlibabaCloudCSAModel
|
||||
from prowler.lib.outputs.finding import Finding
|
||||
|
||||
|
||||
class AlibabaCloudCSA(ComplianceOutput):
|
||||
"""
|
||||
This class represents the Alibaba Cloud CSA compliance output.
|
||||
|
||||
Attributes:
|
||||
- _data (list): A list to store transformed data from findings.
|
||||
- _file_descriptor (TextIOWrapper): A file descriptor to write data to a file.
|
||||
|
||||
Methods:
|
||||
- transform: Transforms findings into Alibaba Cloud CSA compliance format.
|
||||
"""
|
||||
|
||||
def transform(
|
||||
self,
|
||||
findings: list[Finding],
|
||||
compliance: Compliance,
|
||||
compliance_name: str,
|
||||
) -> None:
|
||||
"""
|
||||
Transforms a list of findings into Alibaba Cloud CSA compliance format.
|
||||
|
||||
Parameters:
|
||||
- findings (list): A list of findings.
|
||||
- compliance (Compliance): A compliance model.
|
||||
- compliance_name (str): The name of the compliance model.
|
||||
|
||||
Returns:
|
||||
- None
|
||||
"""
|
||||
for finding in findings:
|
||||
# Get the compliance requirements for the finding
|
||||
finding_requirements = finding.compliance.get(compliance_name, [])
|
||||
for requirement in compliance.Requirements:
|
||||
if requirement.Id in finding_requirements:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = AlibabaCloudCSAModel(
|
||||
Provider=finding.provider,
|
||||
Description=compliance.Description,
|
||||
AccountId=finding.account_uid,
|
||||
Region=finding.region,
|
||||
AssessmentDate=str(timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Name=requirement.Name,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_CCMLite=attribute.CCMLite,
|
||||
Requirements_Attributes_IaaS=attribute.IaaS,
|
||||
Requirements_Attributes_PaaS=attribute.PaaS,
|
||||
Requirements_Attributes_SaaS=attribute.SaaS,
|
||||
Requirements_Attributes_ScopeApplicability=attribute.ScopeApplicability,
|
||||
Status=finding.status,
|
||||
StatusExtended=finding.status_extended,
|
||||
ResourceId=finding.resource_uid,
|
||||
ResourceName=finding.resource_name,
|
||||
CheckId=finding.check_id,
|
||||
Muted=finding.muted,
|
||||
Framework=compliance.Framework,
|
||||
Name=compliance.Name,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
# Add manual requirements to the compliance output
|
||||
for requirement in compliance.Requirements:
|
||||
if not requirement.Checks:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = AlibabaCloudCSAModel(
|
||||
Provider=compliance.Provider.lower(),
|
||||
Description=compliance.Description,
|
||||
AccountId="",
|
||||
Region="",
|
||||
AssessmentDate=str(timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Name=requirement.Name,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_CCMLite=attribute.CCMLite,
|
||||
Requirements_Attributes_IaaS=attribute.IaaS,
|
||||
Requirements_Attributes_PaaS=attribute.PaaS,
|
||||
Requirements_Attributes_SaaS=attribute.SaaS,
|
||||
Requirements_Attributes_ScopeApplicability=attribute.ScopeApplicability,
|
||||
Status="MANUAL",
|
||||
StatusExtended="Manual check",
|
||||
ResourceId="manual_check",
|
||||
ResourceName="Manual check",
|
||||
CheckId="manual",
|
||||
Muted=False,
|
||||
Framework=compliance.Framework,
|
||||
Name=compliance.Name,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
@@ -0,0 +1,96 @@
|
||||
from prowler.config.config import timestamp
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.outputs.compliance.compliance_output import ComplianceOutput
|
||||
from prowler.lib.outputs.compliance.csa.models import AWSCSAModel
|
||||
from prowler.lib.outputs.finding import Finding
|
||||
|
||||
|
||||
class AWSCSA(ComplianceOutput):
|
||||
"""
|
||||
This class represents the AWS CSA compliance output.
|
||||
|
||||
Attributes:
|
||||
- _data (list): A list to store transformed data from findings.
|
||||
- _file_descriptor (TextIOWrapper): A file descriptor to write data to a file.
|
||||
|
||||
Methods:
|
||||
- transform: Transforms findings into AWS CSA compliance format.
|
||||
"""
|
||||
|
||||
def transform(
|
||||
self,
|
||||
findings: list[Finding],
|
||||
compliance: Compliance,
|
||||
compliance_name: str,
|
||||
) -> None:
|
||||
"""
|
||||
Transforms a list of findings into AWS CSA compliance format.
|
||||
|
||||
Parameters:
|
||||
- findings (list): A list of findings.
|
||||
- compliance (Compliance): A compliance model.
|
||||
- compliance_name (str): The name of the compliance model.
|
||||
|
||||
Returns:
|
||||
- None
|
||||
"""
|
||||
for finding in findings:
|
||||
# Get the compliance requirements for the finding
|
||||
finding_requirements = finding.compliance.get(compliance_name, [])
|
||||
for requirement in compliance.Requirements:
|
||||
if requirement.Id in finding_requirements:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = AWSCSAModel(
|
||||
Provider=finding.provider,
|
||||
Description=compliance.Description,
|
||||
AccountId=finding.account_uid,
|
||||
Region=finding.region,
|
||||
AssessmentDate=str(timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Name=requirement.Name,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_CCMLite=attribute.CCMLite,
|
||||
Requirements_Attributes_IaaS=attribute.IaaS,
|
||||
Requirements_Attributes_PaaS=attribute.PaaS,
|
||||
Requirements_Attributes_SaaS=attribute.SaaS,
|
||||
Requirements_Attributes_ScopeApplicability=attribute.ScopeApplicability,
|
||||
Status=finding.status,
|
||||
StatusExtended=finding.status_extended,
|
||||
ResourceId=finding.resource_uid,
|
||||
ResourceName=finding.resource_name,
|
||||
CheckId=finding.check_id,
|
||||
Muted=finding.muted,
|
||||
Framework=compliance.Framework,
|
||||
Name=compliance.Name,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
# Add manual requirements to the compliance output
|
||||
for requirement in compliance.Requirements:
|
||||
if not requirement.Checks:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = AWSCSAModel(
|
||||
Provider=compliance.Provider.lower(),
|
||||
Description=compliance.Description,
|
||||
AccountId="",
|
||||
Region="",
|
||||
AssessmentDate=str(timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Name=requirement.Name,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_CCMLite=attribute.CCMLite,
|
||||
Requirements_Attributes_IaaS=attribute.IaaS,
|
||||
Requirements_Attributes_PaaS=attribute.PaaS,
|
||||
Requirements_Attributes_SaaS=attribute.SaaS,
|
||||
Requirements_Attributes_ScopeApplicability=attribute.ScopeApplicability,
|
||||
Status="MANUAL",
|
||||
StatusExtended="Manual check",
|
||||
ResourceId="manual_check",
|
||||
ResourceName="Manual check",
|
||||
CheckId="manual",
|
||||
Muted=False,
|
||||
Framework=compliance.Framework,
|
||||
Name=compliance.Name,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
@@ -0,0 +1,96 @@
|
||||
from prowler.config.config import timestamp
|
||||
from prowler.lib.check.compliance_models import Compliance
|
||||
from prowler.lib.outputs.compliance.compliance_output import ComplianceOutput
|
||||
from prowler.lib.outputs.compliance.csa.models import AzureCSAModel
|
||||
from prowler.lib.outputs.finding import Finding
|
||||
|
||||
|
||||
class AzureCSA(ComplianceOutput):
|
||||
"""
|
||||
This class represents the Azure CSA compliance output.
|
||||
|
||||
Attributes:
|
||||
- _data (list): A list to store transformed data from findings.
|
||||
- _file_descriptor (TextIOWrapper): A file descriptor to write data to a file.
|
||||
|
||||
Methods:
|
||||
- transform: Transforms findings into Azure CSA compliance format.
|
||||
"""
|
||||
|
||||
def transform(
|
||||
self,
|
||||
findings: list[Finding],
|
||||
compliance: Compliance,
|
||||
compliance_name: str,
|
||||
) -> None:
|
||||
"""
|
||||
Transforms a list of findings into Azure CSA compliance format.
|
||||
|
||||
Parameters:
|
||||
- findings (list): A list of findings.
|
||||
- compliance (Compliance): A compliance model.
|
||||
- compliance_name (str): The name of the compliance model.
|
||||
|
||||
Returns:
|
||||
- None
|
||||
"""
|
||||
for finding in findings:
|
||||
# Get the compliance requirements for the finding
|
||||
finding_requirements = finding.compliance.get(compliance_name, [])
|
||||
for requirement in compliance.Requirements:
|
||||
if requirement.Id in finding_requirements:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = AzureCSAModel(
|
||||
Provider=finding.provider,
|
||||
Description=compliance.Description,
|
||||
SubscriptionId=finding.account_uid,
|
||||
Location=finding.region,
|
||||
AssessmentDate=str(timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Name=requirement.Name,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_CCMLite=attribute.CCMLite,
|
||||
Requirements_Attributes_IaaS=attribute.IaaS,
|
||||
Requirements_Attributes_PaaS=attribute.PaaS,
|
||||
Requirements_Attributes_SaaS=attribute.SaaS,
|
||||
Requirements_Attributes_ScopeApplicability=attribute.ScopeApplicability,
|
||||
Status=finding.status,
|
||||
StatusExtended=finding.status_extended,
|
||||
ResourceId=finding.resource_uid,
|
||||
ResourceName=finding.resource_name,
|
||||
CheckId=finding.check_id,
|
||||
Muted=finding.muted,
|
||||
Framework=compliance.Framework,
|
||||
Name=compliance.Name,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
# Add manual requirements to the compliance output
|
||||
for requirement in compliance.Requirements:
|
||||
if not requirement.Checks:
|
||||
for attribute in requirement.Attributes:
|
||||
compliance_row = AzureCSAModel(
|
||||
Provider=compliance.Provider.lower(),
|
||||
Description=compliance.Description,
|
||||
SubscriptionId="",
|
||||
Location="",
|
||||
AssessmentDate=str(timestamp),
|
||||
Requirements_Id=requirement.Id,
|
||||
Requirements_Description=requirement.Description,
|
||||
Requirements_Name=requirement.Name,
|
||||
Requirements_Attributes_Section=attribute.Section,
|
||||
Requirements_Attributes_CCMLite=attribute.CCMLite,
|
||||
Requirements_Attributes_IaaS=attribute.IaaS,
|
||||
Requirements_Attributes_PaaS=attribute.PaaS,
|
||||
Requirements_Attributes_SaaS=attribute.SaaS,
|
||||
Requirements_Attributes_ScopeApplicability=attribute.ScopeApplicability,
|
||||
Status="MANUAL",
|
||||
StatusExtended="Manual check",
|
||||
ResourceId="manual_check",
|
||||
ResourceName="Manual check",
|
||||
CheckId="manual",
|
||||
Muted=False,
|
||||
Framework=compliance.Framework,
|
||||
Name=compliance.Name,
|
||||
)
|
||||
self._data.append(compliance_row)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user