chore: add provider-uid flag for iac provider (#10233)

Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
This commit is contained in:
Daniel Barranquero
2026-03-03 13:07:15 +01:00
committed by GitHub
parent 8f3e69f571
commit 012fd84cb0
8 changed files with 128 additions and 7 deletions

View File

@@ -38,6 +38,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- OpenStack block storage service with 7 security checks [(#10120)](https://github.com/prowler-cloud/prowler/pull/10120)
- OpenStack compute service with 7 security checks [(#9944)](https://github.com/prowler-cloud/prowler/pull/9944)
- OpenStack image service with 6 security checks [(#10096)](https://github.com/prowler-cloud/prowler/pull/10096)
- IaC `--provider-uid` flag to specify the provider UID for the IaC provider [(#10233)](https://github.com/prowler-cloud/prowler/pull/10233)
### 🔄 Changed

View File

@@ -221,7 +221,8 @@ Detailed documentation at https://docs.prowler.com
action="store_true",
help=(
"Send OCSF output to Prowler Cloud ingestion endpoint. "
"Requires PROWLER_API_KEY environment variable."
"Requires PROWLER_API_KEY environment variable. "
"For the IaC provider, --provider-uid is also required."
),
)

View File

@@ -331,8 +331,9 @@ class Finding(BaseModel):
elif provider.type == "iac":
output_data["auth_method"] = provider.auth_method
output_data["account_uid"] = "iac"
output_data["account_name"] = "iac"
provider_uid = getattr(provider, "provider_uid", None)
output_data["account_uid"] = provider_uid if provider_uid else "iac"
output_data["account_name"] = provider_uid if provider_uid else "iac"
output_data["resource_name"] = getattr(
check_output, "resource_name", ""
)

View File

@@ -273,6 +273,7 @@ class Provider(ABC):
github_username=arguments.github_username,
personal_access_token=arguments.personal_access_token,
oauth_app_token=arguments.oauth_app_token,
provider_uid=arguments.provider_uid,
)
elif "llm" in provider_class_name.lower():
provider_class(

View File

@@ -38,6 +38,7 @@ class IacProvider(Provider):
github_username: str = None,
personal_access_token: str = None,
oauth_app_token: str = None,
provider_uid: str = None,
):
logger.info("Instantiating IAC Provider...")
@@ -47,6 +48,7 @@ class IacProvider(Provider):
self.exclude_path = exclude_path
self.region = "branch"
self.audited_account = "local-iac"
self._provider_uid = provider_uid
self._session = None
self._identity = "prowler"
self._auth_method = "No auth"
@@ -146,6 +148,10 @@ class IacProvider(Provider):
def fixer_config(self):
return self._fixer_config
@property
def provider_uid(self):
return self._provider_uid
def __del__(self):
"""Cleanup temporary directory when provider is destroyed"""
self.cleanup()

View File

@@ -1,3 +1,5 @@
import re
SCANNERS_CHOICES = [
"vuln",
"misconfig",
@@ -68,6 +70,12 @@ def init_parser(self):
default=None,
help="GitHub OAuth app token for authenticated repository cloning. If not provided, will use GITHUB_OAUTH_APP_TOKEN env var.",
)
iac_scan_subparser.add_argument(
"--provider-uid",
dest="provider_uid",
default=None,
help="Unique identifier for the IaC provider. Required when using --export-ocsf.",
)
def validate_arguments(arguments):
@@ -80,4 +88,19 @@ def validate_arguments(arguments):
False,
"--scan-path (-P) and --scan-repository-url (-R) are mutually exclusive. Please specify only one.",
)
export_ocsf = getattr(arguments, "export_ocsf", False)
provider_uid = getattr(arguments, "provider_uid", None)
if export_ocsf and not provider_uid:
return (
False,
"--provider-uid is required when using --export-ocsf with the IAC provider.",
)
if provider_uid and not re.match(
r"^(https?://|git@|ssh://)[^\s/]+[^\s]*\.git$|^(https?://)[^\s/]+[^\s]*$",
provider_uid,
):
return (
False,
"--provider-uid must be a valid repository URL (e.g., https://github.com/user/repo or https://github.com/user/repo.git).",
)
return (True, "")

View File

@@ -689,6 +689,7 @@ class TestFinding:
provider.type = "iac"
provider.scan_repository_url = "https://github.com/user/repo"
provider.auth_method = "No auth"
provider.provider_uid = None
# Mock check result
check_output = MagicMock()

View File

@@ -1,11 +1,11 @@
import types
from prowler.providers.iac.lib.arguments import arguments as iac_arguments
Args = types.SimpleNamespace
def test_validate_arguments_mutual_exclusion():
from prowler.providers.iac.lib.arguments import arguments as iac_arguments
Args = types.SimpleNamespace
# Only scan_path (default)
args = Args(scan_path=".", scan_repository_url=None)
valid, msg = iac_arguments.validate_arguments(args)
@@ -31,3 +31,90 @@ def test_validate_arguments_mutual_exclusion():
valid, msg = iac_arguments.validate_arguments(args)
assert valid
assert msg == ""
def test_validate_arguments_export_ocsf_requires_provider_uid():
# --export-ocsf without provider_uid should fail
args = Args(
scan_path=".",
scan_repository_url=None,
export_ocsf=True,
provider_uid=None,
)
valid, msg = iac_arguments.validate_arguments(args)
assert not valid
assert "--provider-uid is required" in msg
def test_validate_arguments_export_ocsf_with_provider_uid_passes():
# --export-ocsf with valid provider_uid should pass
args = Args(
scan_path=".",
scan_repository_url=None,
export_ocsf=True,
provider_uid="https://github.com/user/repo.git",
)
valid, msg = iac_arguments.validate_arguments(args)
assert valid
assert msg == ""
def test_validate_arguments_no_export_ocsf_without_provider_uid_passes():
# No --export-ocsf, no provider_uid — should pass
args = Args(
scan_path=".",
scan_repository_url=None,
export_ocsf=False,
provider_uid=None,
)
valid, msg = iac_arguments.validate_arguments(args)
assert valid
assert msg == ""
# No export_ocsf attr at all — should pass
args = Args(scan_path=".", scan_repository_url=None)
valid, msg = iac_arguments.validate_arguments(args)
assert valid
assert msg == ""
def test_validate_arguments_provider_uid_must_be_valid_url():
# Invalid provider_uid should fail
args = Args(
scan_path=".",
scan_repository_url=None,
provider_uid="not-a-url",
)
valid, msg = iac_arguments.validate_arguments(args)
assert not valid
assert "valid repository URL" in msg
# HTTPS URL without .git should pass
args = Args(
scan_path=".",
scan_repository_url=None,
provider_uid="https://github.com/user/repo",
)
valid, msg = iac_arguments.validate_arguments(args)
assert valid
assert msg == ""
# HTTPS URL with .git should pass
args = Args(
scan_path=".",
scan_repository_url=None,
provider_uid="https://github.com/user/repo.git",
)
valid, msg = iac_arguments.validate_arguments(args)
assert valid
assert msg == ""
# SSH URL should pass
args = Args(
scan_path=".",
scan_repository_url=None,
provider_uid="git@github.com:user/repo.git",
)
valid, msg = iac_arguments.validate_arguments(args)
assert valid
assert msg == ""