Skip to content

feat(sdk): add sdk_only provider property to hide providers from the app#11578

Open
StylusFrost wants to merge 91 commits into
masterfrom
PROWLER-1799-sdk-only-provider-resolver
Open

feat(sdk): add sdk_only provider property to hide providers from the app#11578
StylusFrost wants to merge 91 commits into
masterfrom
PROWLER-1799-sdk-only-provider-resolver

Conversation

@StylusFrost

@StylusFrost StylusFrost commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Context

With dynamic provider discovery, the API needs the SDK to be the single source of truth for which providers exist, how they are constructed from stored credentials, and which ones belong in the app. This PR collects the SDK contract changes the API layer consumes; the API wiring itself ships separately.

Scope

  • Includes:
    • Provider.sdk_only contract property (default True), Provider.get_app_providers(), sdk_only = False on app-facing built-in providers
    • Compliance discovery merges every entry-point directory a provider contributes (_get_ep_compliance_dirs returns {provider: [paths]}) instead of overwriting
    • Provider.get_scan_arguments(), Provider.get_connection_arguments() and Provider.get_credentials_schema() contract methods
  • Excludes: API consumption of these contracts (separate PRs)

Autonomy

  • CI is expected to pass for this PR branch
  • This PR has one deliverable scope
  • This PR can be rolled back without unrelated changes
  • Tests cover this unit

Description

sdk_only provider property

  • Provider.sdk_only: bool = True on the base contract: a provider (built-in or external) is CLI/SDK-only and hidden from the app until it opts in with sdk_only = False.
  • Provider.get_app_providers(): returns the providers with sdk_only = False (counterpart of get_available_providers(), which keeps listing everything for the CLI). A provider whose class fails to import is treated as sdk_only.
  • The app-exposed built-in providers declare sdk_only = False, so the released behavior is preserved; llm, nhn, scaleway, stackit keep the default and stay hidden.

Multi-package compliance discovery

  • _get_ep_compliance_dirs now accumulates every directory per provider ({provider: [paths]}) so a provider contributed by several external packages has all its compliance frameworks discovered instead of the last one overwriting the rest.

Programmatic construction contract

  • Provider.get_scan_arguments(provider_uid, secret, mutelist_content=None) builds the constructor kwargs from a stored uid plus a secret dict (the shape the API persists), as opposed to the CLI's explicit flags.
  • Provider.get_connection_arguments(provider_uid, secret) builds the test_connection kwargs, which often differ in shape from the constructor.
  • Provider.get_credentials_schema() maps each accepted secret type to the pydantic model that validates it; the default is empty (secret accepted as-is and validated by test_connection).

These base implementations are the defaults; providers override them as needed. The CLI is unaffected. No api/ changes are included in this PR by design.

Steps to review

uv run pytest tests/providers/external/test_dynamic_provider_loading.py -k "SdkOnly or Compliance or BaseContractDefaults"
uv run pytest tests/config/config_test.py

Checklist

SDK/CLI

  • Are there new checks included in this PR? No

License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Summary by CodeRabbit

Release Notes

  • New Features

    • Providers can be marked as hidden from the app experience (while remaining available for CLI/SDK usage).
    • Compliance frameworks contributed for the same provider from multiple external sources are now merged instead of overwritten.
    • Added new provider contract methods to support programmatic generation/validation of scan and connection arguments plus credential schemas.
  • Bug Fixes

    • Improved compliance framework discovery to correctly handle multiple compliance directories per provider.
  • Tests

    • Added and updated regression coverage for provider visibility and compliance-directory merging behavior.

StylusFrost and others added 30 commits April 15, 2026 13:22
…very

  - Implemented dynamic loading of external providers via entry points, allowing for greater flexibility in provider integration.
  - Added functionality to discover compliance directories from entry points, enabling external compliance frameworks to be loaded seamlessly.
  - Refactored check module resolution to prioritize built-in checks while falling back to entry points if necessary.
  - Improved compliance framework loading to include both built-in and external sources, ensuring comprehensive compliance coverage.
  - Enhanced CLI argument parsing to support external providers, improving user experience and configurability.
  - Introduced extensive unit tests to validate dynamic loading, compliance discovery, and overall integration of external providers.
…iders

load_and_validate_config_file only detected the namespaced format for 5
hardcoded providers (aws, gcp, azure, kubernetes, m365). For every other
built-in (github, nhn, vercel, cloudflare, iac, llm, image, mongodbatlas,
oraclecloud, openstack, alibabacloud, googleworkspace) and for any
external plug-in, the full YAML was returned wrapped instead of the
provider's own block.

Replace the hardcoded list with a dynamic check: if the file has a
top-level key matching the provider and its value is a dict, unwrap it.
Keep the legacy flat format for AWS only (historical, pre-multicloud)
and identify it by the absence of nested-dict top-level values, which
prevents cross-provider config leakage when a namespaced file has no
section for the requested provider.
…covery

Conflict in prowler/config/config.py resolved by combining both branches:
- HEAD: external compliance discovery via entry points (PROWLER-1391)
- master: multi-provider framework JSONs scanned at top-level compliance/ (#10300)

Order: built-in per-provider -> built-in multi-provider -> external entry points.
Built-ins first so they win on name collisions against external registrations.

Supporting external plug-ins to register multi-provider frameworks is tracked
in PROWLER-1444.
…er-contract-dynamic-discovery

# Conflicts:
#	prowler/config/config.py
…very

Calling importlib.util.find_spec on prowler.providers.{provider}.services
for an external provider propagates ModuleNotFoundError when the parent
package prowler.providers.{provider} does not exist, instead of returning
None. This caused recover_checks_from_provider, _resolve_check_module and
Scan.scan to fail with "No module named 'prowler.providers.{external}'"
even though the plug-in registered its checks via entry points correctly.

Gate the built-in branch on Provider.is_builtin (which already wraps the
find_spec in try/except) and reuse _resolve_check_module from Scan.scan
so external providers fall through to the entry-point lookup.
@github-actions github-actions Bot added provider/azure Issues/PRs related with the Azure provider provider/gcp Issues/PRs related with the Google Cloud Platform provider provider/kubernetes Issues/PRs related with the Kubernetes provider provider/github Issues/PRs related with the Github provider provider/m365 Issues/PRs related with the M365 provider provider/iac Issues/PRs related with the IaC provider provider/mongodbatlas Issues/PRs related with the Mongo DB Atlas provider provider/oci Issues/PRs related with the OCI provider provider/cloudflare Issues/PRs related with the Cloudflare provider provider/alibabacloud Issues/PRs related with the Alibaba Cloud provider provider/openstack Issues/PRs related with the OpenStack provider provider/googleworkspace Issues/PRs related with the Google Workspace provider provider/vercel provider/okta labels Jun 17, 2026
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Conflict Markers Resolved

All conflict markers have been successfully resolved in this pull request.

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

✅ All necessary CHANGELOG.md files have been updated.

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

🔒 Container Security Scan

Image: prowler:d626236
Last scan: 2026-06-17 16:38:16 UTC

✅ No Vulnerabilities Detected

The container image passed all security checks. No known CVEs were found.

📋 Resources:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@prowler/providers/common/provider.py`:
- Around line 240-241: The condition at the mutelist_content assignment uses a
truthy check that will drop empty dictionaries since they evaluate to False in
Python. This prevents `mutelist_content={}` from being passed to kwargs, which
can unintentionally trigger provider-side default mutelist loading. Replace the
truthy check `if mutelist_content:` with an explicit None check `if
mutelist_content is not None:` so that empty dictionary payloads are preserved
and included in kwargs.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8a101223-f338-42ae-94eb-bce163999a1a

📥 Commits

Reviewing files that changed from the base of the PR and between e2ce41a and b88c304.

📒 Files selected for processing (21)
  • prowler/CHANGELOG.md
  • prowler/config/config.py
  • prowler/providers/alibabacloud/alibabacloud_provider.py
  • prowler/providers/aws/aws_provider.py
  • prowler/providers/azure/azure_provider.py
  • prowler/providers/cloudflare/cloudflare_provider.py
  • prowler/providers/common/provider.py
  • prowler/providers/gcp/gcp_provider.py
  • prowler/providers/github/github_provider.py
  • prowler/providers/googleworkspace/googleworkspace_provider.py
  • prowler/providers/iac/iac_provider.py
  • prowler/providers/image/image_provider.py
  • prowler/providers/kubernetes/kubernetes_provider.py
  • prowler/providers/m365/m365_provider.py
  • prowler/providers/mongodbatlas/mongodbatlas_provider.py
  • prowler/providers/okta/okta_provider.py
  • prowler/providers/openstack/openstack_provider.py
  • prowler/providers/oraclecloud/oraclecloud_provider.py
  • prowler/providers/vercel/vercel_provider.py
  • tests/config/config_test.py
  • tests/providers/external/test_dynamic_provider_loading.py

Comment thread prowler/providers/common/provider.py Outdated
@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.65217% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.22%. Comparing base (0463cd1) to head (e3e90c3).
⚠️ Report is 28 commits behind head on master.

❗ There is a different number of reports uploaded between BASE (0463cd1) and HEAD (e3e90c3). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (0463cd1) HEAD (e3e90c3)
api 1 0
Additional details and impacted files
@@             Coverage Diff             @@
##           master   #11578       +/-   ##
===========================================
- Coverage   94.11%   79.22%   -14.90%     
===========================================
  Files         247     1878     +1631     
  Lines       36408    63187    +26779     
===========================================
+ Hits        34265    50058    +15793     
- Misses       2143    13129    +10986     
Flag Coverage Δ
api ?
prowler-py3.10-aws 90.11% <100.00%> (?)
prowler-py3.10-azure 89.61% <100.00%> (?)
prowler-py3.10-config 79.21% <95.65%> (?)
prowler-py3.10-external 79.17% <95.65%> (?)
prowler-py3.10-gcp 90.04% <100.00%> (?)
prowler-py3.10-github 89.35% <100.00%> (?)
prowler-py3.10-googleworkspace 88.30% <100.00%> (?)
prowler-py3.10-iac 89.37% <100.00%> (?)
prowler-py3.10-kubernetes 89.71% <100.00%> (?)
prowler-py3.10-m365 89.59% <100.00%> (?)
prowler-py3.10-mongodbatlas 89.27% <100.00%> (?)
prowler-py3.10-okta 89.71% <100.00%> (?)
prowler-py3.10-openstack 87.91% <100.00%> (?)
prowler-py3.10-oraclecloud 87.70% <100.00%> (?)
prowler-py3.10-vercel 88.13% <100.00%> (?)
prowler-py3.11-aws 90.11% <100.00%> (?)
prowler-py3.11-azure 89.61% <100.00%> (?)
prowler-py3.11-config 79.16% <95.65%> (?)
prowler-py3.11-external 79.12% <95.65%> (?)
prowler-py3.11-gcp 90.04% <100.00%> (?)
prowler-py3.11-github 89.35% <100.00%> (?)
prowler-py3.11-googleworkspace 88.24% <100.00%> (?)
prowler-py3.11-iac 89.29% <100.00%> (?)
prowler-py3.11-kubernetes 89.70% <100.00%> (?)
prowler-py3.11-m365 89.51% <100.00%> (?)
prowler-py3.11-mongodbatlas 89.20% <100.00%> (?)
prowler-py3.11-okta 89.71% <100.00%> (?)
prowler-py3.11-openstack 87.84% <100.00%> (?)
prowler-py3.11-oraclecloud 87.63% <100.00%> (?)
prowler-py3.11-vercel 88.07% <100.00%> (?)
prowler-py3.12-aws 90.12% <100.00%> (?)
prowler-py3.12-azure 89.61% <100.00%> (?)
prowler-py3.12-config 79.20% <95.65%> (?)
prowler-py3.12-external 79.16% <95.65%> (?)
prowler-py3.12-gcp 90.04% <100.00%> (?)
prowler-py3.12-github 89.35% <100.00%> (?)
prowler-py3.12-googleworkspace 88.29% <100.00%> (?)
prowler-py3.12-iac 89.35% <100.00%> (?)
prowler-py3.12-kubernetes 89.70% <100.00%> (?)
prowler-py3.12-m365 89.57% <100.00%> (?)
prowler-py3.12-mongodbatlas 89.26% <100.00%> (?)
prowler-py3.12-okta 89.71% <100.00%> (?)
prowler-py3.12-openstack 87.90% <100.00%> (?)
prowler-py3.12-oraclecloud 87.69% <100.00%> (?)
prowler-py3.12-vercel 88.12% <100.00%> (?)
prowler-py3.13-aws 90.10% <100.00%> (?)
prowler-py3.13-azure 89.60% <100.00%> (?)
prowler-py3.13-config 79.20% <95.65%> (?)
prowler-py3.13-external 79.16% <95.65%> (?)
prowler-py3.13-gcp 90.03% <100.00%> (?)
prowler-py3.13-github 89.34% <100.00%> (?)
prowler-py3.13-googleworkspace 88.29% <100.00%> (?)
prowler-py3.13-iac 89.35% <100.00%> (?)
prowler-py3.13-kubernetes 89.70% <100.00%> (?)
prowler-py3.13-m365 89.57% <100.00%> (?)
prowler-py3.13-mongodbatlas 89.25% <100.00%> (?)
prowler-py3.13-okta 89.70% <100.00%> (?)
prowler-py3.13-openstack 87.89% <100.00%> (?)
prowler-py3.13-oraclecloud 87.68% <100.00%> (?)
prowler-py3.13-vercel 88.11% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
prowler 79.22% <98.55%> (∅)
api ∅ <ø> (∅)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

⚠️ Branch updated during autofix.

The branch was updated while autofix was in progress. Please try again.

Use an explicit None check so an empty mutelist_content dict is passed through instead of being dropped, which could trigger provider-side default mutelist loading.

@danibarranqueroo danibarranqueroo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linode was added as new provider, since it's sdk_only I think that is not needed to add that flag into the provider code, but please review it. Thanks!

@StylusFrost

Copy link
Copy Markdown
Contributor Author

Confirmed, nothing to add for Linode. Every provider defaults to sdk_only = True in the base Provider class, and a provider only becomes visible to the app (API/UI) by explicitly opting in with sdk_only = False. Since Linode is SDK-only, it inherits the default and the resolver already excludes it from get_app_providers(). Thanks for double-checking!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

provider/alibabacloud Issues/PRs related with the Alibaba Cloud provider provider/aws Issues/PRs related with the AWS provider provider/azure Issues/PRs related with the Azure provider provider/cloudflare Issues/PRs related with the Cloudflare provider provider/gcp Issues/PRs related with the Google Cloud Platform provider provider/github Issues/PRs related with the Github provider provider/googleworkspace Issues/PRs related with the Google Workspace provider provider/iac Issues/PRs related with the IaC provider provider/kubernetes Issues/PRs related with the Kubernetes provider provider/m365 Issues/PRs related with the M365 provider provider/mongodbatlas Issues/PRs related with the Mongo DB Atlas provider provider/oci Issues/PRs related with the OCI provider provider/okta provider/openstack Issues/PRs related with the OpenStack provider provider/vercel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants