diff --git a/prowler/lib/check/check.py b/prowler/lib/check/check.py
index 8bb50d8871..40eefc0de6 100644
--- a/prowler/lib/check/check.py
+++ b/prowler/lib/check/check.py
@@ -747,6 +747,9 @@ def execute(
is_finding_muted_args["tenancy_id"] = (
global_provider.identity.tenancy_id
)
+ else:
+ # External/custom provider — delegate identity args
+ is_finding_muted_args = global_provider.get_mutelist_finding_args()
for finding in check_findings:
if global_provider.type == "cloudflare":
is_finding_muted_args["account_id"] = finding.account_id
diff --git a/prowler/providers/common/provider.py b/prowler/providers/common/provider.py
index 8613957a68..df7d5997aa 100644
--- a/prowler/providers/common/provider.py
+++ b/prowler/providers/common/provider.py
@@ -193,6 +193,17 @@ class Provider(ABC):
f"{self.__class__.__name__} has not implemented generate_compliance_output()"
)
+ def get_mutelist_finding_args(self) -> dict:
+ """Return extra kwargs for mutelist.is_finding_muted() besides 'finding'.
+
+ External providers must return a dict with the identity key their
+ Mutelist subclass expects, e.g. ``{"account_id": self.identity.account_id}``.
+ The ``finding`` kwarg is added automatically by the caller.
+ """
+ raise NotImplementedError(
+ f"{self.__class__.__name__} has not implemented get_mutelist_finding_args()"
+ )
+
@property
def is_external_tool_provider(self) -> bool:
"""True for providers that delegate scanning to an external tool."""
diff --git a/tests/providers/external/test_dynamic_provider_loading.py b/tests/providers/external/test_dynamic_provider_loading.py
index c7c705d17a..64b9ffdb67 100644
--- a/tests/providers/external/test_dynamic_provider_loading.py
+++ b/tests/providers/external/test_dynamic_provider_loading.py
@@ -84,6 +84,9 @@ class FakeExternalProvider(Provider):
"region": "local",
}
+ def get_mutelist_finding_args(self):
+ return {"host_id": self.identity.host_id}
+
def get_html_assessment_summary(self):
return "
Fake Assessment
"
@@ -1158,7 +1161,60 @@ class TestBaseContractDefaults:
with pytest.raises(NotImplementedError):
provider.generate_compliance_output([], {}, set(), MagicMock(), {})
+ def test_get_mutelist_finding_args_raises_not_implemented(self):
+ """Base Provider.get_mutelist_finding_args raises NotImplementedError."""
+ provider = FakeProviderNoHelpText()
+ with pytest.raises(NotImplementedError):
+ provider.get_mutelist_finding_args()
+
def test_is_external_tool_provider_defaults_to_false(self):
"""Base Provider.is_external_tool_provider returns False."""
provider = FakeProviderNoHelpText()
assert provider.is_external_tool_provider is False
+
+
+# ===========================================================================
+# 10. Mutelist Dispatch for External Providers
+# ===========================================================================
+
+
+class TestMutelistDispatch:
+ """Tests for mutelist integration with external providers."""
+
+ def test_get_mutelist_finding_args_returns_identity(self, fake_provider):
+ """External provider returns identity kwargs for mutelist."""
+ args = fake_provider.get_mutelist_finding_args()
+
+ assert args == {"host_id": "fake-host-1"}
+
+ def test_mutelist_dispatch_calls_external_provider(self, fake_provider):
+ """execute() uses get_mutelist_finding_args for unknown provider types."""
+ from prowler.lib.check.check import execute
+
+ # Create a mock check that returns one finding
+ finding = MagicMock()
+ finding.status = "FAIL"
+ finding.muted = False
+ finding.check_metadata.Provider = "fakeexternal"
+
+ check = MagicMock()
+ check.execute.return_value = [finding]
+ check.CheckID = "fake_check"
+ check.ServiceName = "fake_service"
+ check.Severity.value = "high"
+
+ # Setup mutelist on the provider
+ fake_provider.mutelist = MagicMock()
+ fake_provider.mutelist.mutelist = {"Accounts": {}}
+ fake_provider.mutelist.is_finding_muted.return_value = True
+
+ output_options = MagicMock()
+ output_options.status = []
+ output_options.unix_timestamp = False
+
+ execute(check, fake_provider, None, output_options)
+
+ # is_finding_muted should have been called with host_id + finding
+ fake_provider.mutelist.is_finding_muted.assert_called_once_with(
+ host_id="fake-host-1", finding=finding
+ )