Skip to content

logging_sink_created false-FAILs for projects covered only by org/folder-level aggregated sinks #11343

@alinealfa

Description

@alinealfa

Issue search

  • I have searched the existing issues and this bug has not been reported yet

Which component is affected?

Prowler CLI/SDK

Cloud Provider

GCP

Steps to Reproduce

  1. Under an organization that uses only an org-level (or folder-level) aggregated log sink with includeChildren=True — i.e. a single sink at the org node routes all descendant projects' logs into one destination, no per-project sinks — verify the topology:

    gcloud logging sinks list --organization=<ORG_ID> \
      --format='table(name,destination,filter:wrap=40,includeChildren)'
    

    At least one sink with INCLUDE_CHILDREN=True should be present.

  2. Run Prowler against any project under that org:

    prowler gcp --project-id <project-id> --check logging_sink_created
    

Expected behavior

PASS — the project's logs are exported because an ancestor aggregated sink (includeChildren=True) covers it. This is the GCP-recommended pattern for centralized logging, and is the architecture used by terraform-example-foundation via the log-export module's parent_resource_type = "organization" + include_children = true configuration.

Actual Result with Screenshots or Logs

FAILThere are no logging sinks to export copies of all the log entries in project <project-id>.

Root cause: prowler/providers/gcp/services/logging/logging_sink_created/logging_sink_created.py (and the underlying service in logging_service.py) only enumerates project-level sinks via logging.projects.sinks.list. It doesn't query logging.organizations.sinks.list or logging.folders.sinks.list, and it has no notion of includeChildren=True coverage of descendant projects.

for sink in logging_client.sinks:
    if sink.filter == "all":
        projects_with_logging_sink[sink.project_id] = sink

logging_client.sinks only contains project-scope sinks. Org/folder aggregated sinks are invisible to the check. Every project under an org that uses centralized aggregation gets a false-FAIL.

Why this is a real problem

Centralized aggregation via org/folder-level sinks is the GCP-recommended pattern for organizations larger than a single project. It's documented here and used by:

Any organization following these patterns trips this check on every project they own. The check is enforcing an old pattern of one-sink-per-project, before GCP introduced aggregated sinks (~2019).

How did you install Prowler?

Docker (docker pull toniblyx/prowler)

Environment Resource

GitHub Actions ubuntu-24.04 runner; also reproduced locally via docker run prowlercloud/prowler:v5.28.0 gcp ….

OS used

Ubuntu 24.04 (GHA runner) / macOS.

Prowler version

5.28.0

Python version

3.12 (bundled in prowlercloud/prowler:v5.28.0)

Pip version

N/A — Docker image install.

Context

Suggested fix — also enumerate organizations.sinks.list and folders.sinks.list, build the resource hierarchy (project → folder ancestors → org), and treat a project as covered if any ancestor's sink has includeChildren=True and matches the inclusive-filter test (filter == "all" or empty).

Sketch:

# In LoggingService:
def _get_sinks(self):
    # existing: project-scope sinks
    for project_id in self.project_ids:
        ...

    # NEW: org-scope sinks
    org_sinks = self.client.organizations().sinks().list(
        parent=f"organizations/{self.organization_id}"
    ).execute().get("sinks", [])
    self.aggregated_sinks.extend(s for s in org_sinks if s.get("includeChildren"))

    # NEW: folder-scope sinks (per ancestor folder of each project)
    ...

# In logging_sink_created.execute():
for project in self.project_ids:
    if has_project_level_all_sink(project): PASS
    elif covered_by_ancestor_aggregated_sink(project): PASS
    else: FAIL

The same pattern affects related per-project logging checks (the logging_log_metric_filter_and_alert_* family relies on per-project metric filters + alert policies, which can also be centrally implemented). A more general fix would be to surface a "covered by ancestor aggregated control" notion that those checks can also consume.

Happy to open a PR for the sink-aggregation case if there's appetite for the approach.

Metadata

Metadata

Labels

bugprovider/gcpIssues/PRs related with the Google Cloud Platform providerseverity/lowBug won't result in any noticeable breakdown of the execution.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions