Skip to content

Conversation

@AdriiiPRodri
Copy link
Contributor

@AdriiiPRodri AdriiiPRodri commented Dec 26, 2025

Context

This PR adds a new Resource Group API feature that enables grouping and filtering findings by logical resource categories (storage, compute, security, database, etc.). This provides users with a high-level view of their security posture organized by resource type, complementing the existing categories functionality.

Related to the Resource Inventory component requirements.

Description

Add support for resource group aggregations to enable grouping findings by logical resource categories. This feature mirrors the existing categories implementation pattern.

Changes:

API:

  • Add ScanResourceGroupSummary model for pre-aggregated metrics per scan
  • Add group field to Finding, Resource, and ScanResourceGroupSummary models
  • Add /api/v1/overviews/groups endpoint with provider filtering
  • Add group and group__in filters to findings and resources endpoints
  • Include groups in metadata responses
  • Add aggregate_resource_group_counts for real-time scan aggregation
  • Add backfill_scan_resource_group_summaries task for historical data
  • Add ResourceGroupOverviewFilter and ResourceGroupOverviewSerializer

The endpoint returns aggregated counts (total_findings, failed_findings, new_failed_findings, resources_count) grouped by group and severity, following the same pattern as the categories endpoint.

Field naming convention (consistent with existing API patterns):

Model Field (singular) Metadata (plural) Filter
group groups group, group__in

Manual Testing Performed

All endpoints verified working:

  • GET /api/v1/findings → returns group field
  • GET /api/v1/resources → returns group field
  • GET /api/v1/findings?filter[group]=monitoring → filter works
  • GET /api/v1/findings?filter[group__in]=monitoring,security → multi-filter works
  • GET /api/v1/overviews/groups → returns group aggregations
  • GET /api/v1/findings/metadata → returns groups (plural)

All 1426 API tests pass.

Steps to review

  1. Review the new migration 0066_finding_resource_group_scanresourcegroupsummary.py
  2. Test the new endpoint:
    • GET /api/v1/overviews/groups - returns all resource groups
    • GET /api/v1/overviews/groups?filter[group]=storage - filter by specific group
    • GET /api/v1/overviews/groups?filter[provider_type]=aws - filter by provider
  3. Test findings filter:
    • GET /api/v1/findings?filter[group]=storage
    • GET /api/v1/findings?filter[group__in]=storage,security
  4. Verify metadata endpoint includes groups:
    • GET /api/v1/findings/metadata
    • GET /api/v1/resources/metadata
  5. Run a scan and verify ScanResourceGroupSummary records are created
  6. Verify resources_count correctly counts unique resources (not total findings)

Checklist

  • Are there new checks included in this PR? No
  • Review if the code is being covered by tests.
  • Review if code is being documented following this specification
  • Review if backport is needed.
  • Review if is needed to change the Readme.md
  • Ensure new entries are added to CHANGELOG.md, if applicable.

UI

  • N/A (API-only changes)

API

  • Verify if API specs need to be regenerated.
  • Check if version updates are required (e.g., specs, Poetry, etc.).
  • Ensure new entries are added to CHANGELOG.md, if applicable.

License

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

@AdriiiPRodri AdriiiPRodri requested review from a team as code owners December 26, 2025 11:27
@github-actions github-actions bot added documentation component/api review-django-migrations This PR contains changes in Django migrations labels Dec 26, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Dec 26, 2025

✅ All necessary CHANGELOG.md files have been updated.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 26, 2025

Conflict Markers Resolved

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

@github-actions
Copy link
Contributor

github-actions bot commented Dec 26, 2025

🔒 Container Security Scan

Image: prowler-api:afe2812
Last scan: 2026-01-14 15:34:53 UTC

📊 Vulnerability Summary

Severity Count
🔴 Critical 11
Total 11

10 package(s) affected

⚠️ Action Required

Critical severity vulnerabilities detected. These should be addressed before merging:

  • Review the detailed scan results
  • Update affected packages to patched versions
  • Consider using a different base image if updates are unavailable

📋 Resources:

@codecov
Copy link

codecov bot commented Dec 26, 2025

Codecov Report

❌ Patch coverage is 96.00000% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.57%. Comparing base (864b209) to head (a3581f5).
⚠️ Report is 13 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master    #9694       +/-   ##
===========================================
+ Coverage    3.94%   92.57%   +88.62%     
===========================================
  Files         830      165      -665     
  Lines       23425    23608      +183     
===========================================
+ Hits          925    21854    +20929     
+ Misses      22500     1754    -20746     
Flag Coverage Δ
api 92.57% <96.00%> (?)
prowler-py3.10-aws ?
prowler-py3.11-aws ?
prowler-py3.12-aws ?
prowler-py3.9-aws ?

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

Components Coverage Δ
prowler ∅ <ø> (∅)
api 92.57% <96.00%> (∅)
🚀 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.

Copy link
Member

@jfagoagas jfagoagas left a comment

Choose a reason for hiding this comment

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

That's great @AdriiiPRodri I did a first pass without a functional test and left some comments/suggestions. Thanks!

- Add resource_group field to Resource model (0067 migration)
- Add backfill migration to populate from findings (0068 migration)
- Add resource_group filter (exact, in) to ResourceFilter
- Add resource_group to ResourceSerializer
- Update API spec with resource_group field and filter params
- Populate resource_group from check metadata in scan job
- Add tests for resource_group filter and field presence in response
Remove migration 0068 that backfills resource_group from findings.
This operation is too expensive for production databases.

The resource_group field will only be populated for new resources
during scans. Existing resources will have NULL until re-scanned.
- Add resource_groups field to ResourceMetadataSerializer
- Add resource_groups query to /resources/metadata endpoint
- Add resource_groups query to /resources/metadata/latest endpoint
- Add resource_group filter to LatestResourceFilter
Copy link
Member

@jfagoagas jfagoagas left a comment

Choose a reason for hiding this comment

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

After our conversation:

  • Rename resource_group to group
  • Mark resource_type as deprecated in the API spec

- Rename resource_group field to group in Resource model
- Update migration 0067 with new field name
- Update ResourceSerializer and ResourceMetadataSerializer
- Update filters (ResourceFilter, LatestResourceFilter)
- Update views metadata queries
- Update scan.py Resource creation/update logic
- Update fixtures and tests
- Update API spec with new field names and filters
- Mark resource type field as deprecated in API spec
- Add groups assertions to resources metadata tests
Copy link
Contributor Author

@AdriiiPRodri AdriiiPRodri left a comment

Choose a reason for hiding this comment

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

I think there's a bit of confusion between group and groups, at least for now. That said, I don't think this should change: resources should belong to a single group only

@jfagoagas
Copy link
Member

I think there's a bit of confusion between group and groups, at least for now. That said, I don't think this should change: resources should belong to a single group only

That's correct. The relationship is 1:1.

@Alan-TheGentleman
Copy link
Contributor

I think there's a bit of confusion between group and groups, at least for now. That said, I don't think this should change: resources should belong to a single group only

But I have a question:

The singular/plural pattern already exists throughout the system:

Model Field (singular) Metadata (plural) Filter
Resource region regions region__in
Resource service services service__in
Resource type types type__in
Resource group groups group__in
Finding region regions region__in
Finding service services service__in
Finding severity severities severity__in
Finding category categories category__in
Finding resource_group resource_groups resource_group__in

The logic is:

  • Singular → the field in the model (each record has ONE value)
  • Plural → the list of unique values in metadata endpoint (to populate dropdowns)
  • __in filter → allows filtering by multiple values even though each record has only one

This is consistent with Django/DRF conventions and how the rest of the API already works.

Maybe I don't have enough context (sorry in advance)


@AdriiiPRodri
Copy link
Contributor Author

AdriiiPRodri commented Jan 8, 2026

I think there's a bit of confusion between group and groups, at least for now. That said, I don't think this should change: resources should belong to a single group only

But I have a question:

The singular/plural pattern already exists throughout the system:
Model Field (singular) Metadata (plural) Filter
Resource region regions region__in
Resource service services service__in
Resource type types type__in
Resource group groups group__in
Finding region regions region__in
Finding service services service__in
Finding severity severities severity__in
Finding category categories category__in
Finding resource_group resource_groups resource_group__in

The logic is:

* **Singular** → the field in the model (each record has ONE value)

* **Plural** → the list of unique values in metadata endpoint (to populate dropdowns)

* **`__in` filter** → allows filtering by multiple values even though each record has only one

This is consistent with Django/DRF conventions and how the rest of the API already works.

Maybe I don't have enough context (sorry in advance)

I mean that if it is a 1:1 relationship, the API output must use the singular form. If it is already like that, there is nothing to change. While testing the changes, I see several things: in findings the change from resource_group to group has not been applied, in resources, after the changes, group appears as null. However, in findings, where resource_group is still present, the new category does appear (still using resource_group)

Findings:

{
      "type": "findings",
      "id": "019b9d28-f3c7-7d79-bd40-44758ededb27",
      "attributes": {
        "uid": "prowler-aws-accessanalyzer_enabled-106908755756-ap-northeast-2-analyzer/unknown",
        "delta": "new",
        "status": "FAIL",
        "status_extended": "IAM Access Analyzer in account 106908755756 is not enabled.",
        "severity": "low",
        "check_id": "accessanalyzer_enabled",
        "check_metadata": {
          "risk": "Without an active analyzer, visibility into unintended public, cross-account, or risky internal access is lost. Adversaries can exploit exposed S3, snapshots, KMS keys, or permissive role trusts for data exfiltration and escalation. Unused permissions persist, enlarging the attack surface. This degrades confidentiality and integrity.",
          "notes": "",
          "checkid": "accessanalyzer_enabled",
          "provider": "aws",
          "severity": "low",
          "checktype": [
            "Software and Configuration Checks/AWS Security Best Practices",
            "Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices"
          ],
          "dependson": [],
          "relatedto": [],
          "categories": [
            "identity-access",
            "trust-boundaries"
          ],
          "checktitle": "IAM Access Analyzer is enabled",
          "compliance": [],
          "relatedurl": "",
          "description": "**IAM Access Analyzer** presence and status are evaluated per account and Region. An analyzer in `ACTIVE` state indicates continuous analysis of supported resources and IAM activity to identify external, internal, and unused access.",
          "remediation": {
            "code": {
              "cli": "aws accessanalyzer create-analyzer --analyzer-name example_resource --type ACCOUNT",
              "other": "1. In the AWS Console, open IAM\n2. Go to Access analyzer > Analyzer settings\n3. Confirm the desired Region\n4. Click Create analyzer\n5. Select Resource analysis - External access\n6. Set Name to \"example_resource\" and Zone of trust to \"Current account\"\n7. Click Create",
              "nativeiac": "```yaml\nResources:\n  example_resource:\n    Type: AWS::AccessAnalyzer::Analyzer  # This resource enables IAM Access Analyzer\n    Properties:\n      AnalyzerName: example_resource\n      Type: ACCOUNT  # This line fixes the security issue\n```",
              "terraform": "```hcl\nresource \"aws_accessanalyzer_analyzer\" \"example_resource\" {\n  analyzer_name = \"example_resource\"\n  type          = \"ACCOUNT\" # This line fixes the security issue\n}\n```"
            },
            "recommendation": {
              "url": "https://hub.prowler.com/check/accessanalyzer_enabled",
              "text": "Enable **IAM Access Analyzer** across all accounts and active Regions (*or organization-wide*). Operate on least privilege: continuously review findings, remove unintended access, and trim unused permissions. Use archive rules sparingly, integrate reviews into change/CI/CD workflows, and enforce separation of duties on policy changes."
            }
          },
          "servicename": "accessanalyzer",
          "checkaliases": [],
          "resourcetype": "Other",
          "resourcegroup": "security",
          "additionalurls": [
            "https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-manage-external.html",
            "https://aws.amazon.com/iam/access-analyzer/",
            "https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-getting-started.html",
            "https://docs.aws.amazon.com/access-analyzer/latest/APIReference/API_CreateAnalyzer.html",
            "https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html",
            "https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-create-external.html",
            "https://docs.aws.amazon.com/access-analyzer/latest/APIReference/Welcome.html",
            "https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-create-internal.html"
          ],
          "subservicename": "",
          "resourceidtemplate": ""
        },
        "categories": [
          "identity-access",
          "trust-boundaries"
        ],
        "resource_group": "security",
        "raw_result": {},
        "inserted_at": "2026-01-08T10:31:05.416673Z",
        "updated_at": "2026-01-08T10:31:05.416675Z",
        "first_seen_at": "2026-01-08T10:31:05.415317Z",
        "muted": false,
        "muted_reason": null
      },
      "relationships": {
        "scan": {
          "data": {
            "type": "scans",
            "id": "019b9d28-9fa5-7a6e-afee-f43f0131f7a6"
          }
        },
        "resources": {
          "meta": {
            "count": 1
          },
          "data": [
            {
              "type": "resources",
              "id": "cba12781-aec2-4866-b7c3-1f4fd834a5fd"
            }
          ]
        }
      },
      "links": {
        "self": "http://localhost:8080/api/v1/findings/019b9d28-f3c7-7d79-bd40-44758ededb27"
      }
    }

Resources:

{
      "type": "resources",
      "id": "cba12781-aec2-4866-b7c3-1f4fd834a5fd",
      "attributes": {
        "inserted_at": "2026-01-08T10:31:05.414231Z",
        "updated_at": "2026-01-08T10:31:05.414237Z",
        "uid": "arn:aws:accessanalyzer:ap-northeast-2:106908755756:analyzer/unknown",
        "name": "analyzer/unknown",
        "region": "ap-northeast-2",
        "service": "accessanalyzer",
        "tags": {},
        "failed_findings_count": 1,
        "metadata": "{\"arn\": \"arn:aws:accessanalyzer:ap-northeast-2:106908755756:analyzer/unknown\", \"name\": \"analyzer/unknown\", \"status\": \"NOT_AVAILABLE\", \"findings\": [], \"tags\": [], \"type\": \"\", \"region\": \"ap-northeast-2\"}",
        "details": "",
        "partition": "aws",
        "group": null,
        "type": "Other"
      },
      "relationships": {
        "provider": {
          "data": {
            "type": "providers",
            "id": "6d03e61c-a326-4f30-adbe-72d41beded59"
          }
        },
        "findings": {
          "data": [
            {
              "type": "findings",
              "id": "019b9d28-f3c7-7d79-bd40-44758ededb27"
            }
          ],
          "meta": {
            "count": 1
          }
        }
      },
      "links": {
        "self": "http://localhost:8080/api/v1/resources/cba12781-aec2-4866-b7c3-1f4fd834a5fd"
      }
    }

…ary models

Rename the field from 'resource_group' to 'group' for consistency with the
API naming conventions (singular for model fields, plural for metadata).

Changes:
- Models: Finding.group, ScanResourceGroupSummary.group (migration updated)
- Filters: filter[group], filter[group__in]
- Serializers: 'group' field in responses
- Views: /api/v1/overviews/groups endpoint (was /resource_groups)
- Metadata: 'groups' (plural) in metadata responses
- Jobs: scan.py and backfill.py updated to use 'group'
- Tests: All fixtures and assertions updated

Manual testing performed:
- GET /api/v1/findings -> returns 'group' field ✓
- GET /api/v1/resources -> returns 'group' field ✓
- GET /api/v1/findings?filter[group]=monitoring -> works ✓
- GET /api/v1/findings?filter[group__in]=monitoring,security -> works ✓
- GET /api/v1/overviews/groups -> returns group aggregations ✓
- GET /api/v1/findings/metadata -> returns 'groups' (plural) ✓

All 1426 API tests pass.

Note for existing deployments: If migration 0066 was already applied with
the old column name 'resource_group', run:
  ALTER TABLE findings RENAME COLUMN resource_group TO "group";
  ALTER TABLE scan_resource_group_summaries RENAME COLUMN resource_group TO "group";
@Alan-TheGentleman
Copy link
Contributor

I think there's a bit of confusion between group and groups, at least for now. That said, I don't think this should change: resources should belong to a single group only

But I have a question:
The singular/plural pattern already exists throughout the system:
Model Field (singular) Metadata (plural) Filter
Resource region regions region__in
Resource service services service__in
Resource type types type__in
Resource group groups group__in
Finding region regions region__in
Finding service services service__in
Finding severity severities severity__in
Finding category categories category__in
Finding resource_group resource_groups resource_group__in
The logic is:

* **Singular** → the field in the model (each record has ONE value)

* **Plural** → the list of unique values in metadata endpoint (to populate dropdowns)

* **`__in` filter** → allows filtering by multiple values even though each record has only one

This is consistent with Django/DRF conventions and how the rest of the API already works.
Maybe I don't have enough context (sorry in advance)

I mean that if it is a 1:1 relationship, the API output must use the singular form. If it is already like that, there is nothing to change. While testing the changes, I see several things: in findings the change from resource_group to group has not been applied, in resources, after the changes, group appears as null. However, in findings, where resource_group is still present, the new category does appear (still using resource_group)

Findings:

{
      "type": "findings",
      "id": "019b9d28-f3c7-7d79-bd40-44758ededb27",
      "attributes": {
        "uid": "prowler-aws-accessanalyzer_enabled-106908755756-ap-northeast-2-analyzer/unknown",
        "delta": "new",
        "status": "FAIL",
        "status_extended": "IAM Access Analyzer in account 106908755756 is not enabled.",
        "severity": "low",
        "check_id": "accessanalyzer_enabled",
        "check_metadata": {
          "risk": "Without an active analyzer, visibility into unintended public, cross-account, or risky internal access is lost. Adversaries can exploit exposed S3, snapshots, KMS keys, or permissive role trusts for data exfiltration and escalation. Unused permissions persist, enlarging the attack surface. This degrades confidentiality and integrity.",
          "notes": "",
          "checkid": "accessanalyzer_enabled",
          "provider": "aws",
          "severity": "low",
          "checktype": [
            "Software and Configuration Checks/AWS Security Best Practices",
            "Software and Configuration Checks/Industry and Regulatory Standards/AWS Foundational Security Best Practices"
          ],
          "dependson": [],
          "relatedto": [],
          "categories": [
            "identity-access",
            "trust-boundaries"
          ],
          "checktitle": "IAM Access Analyzer is enabled",
          "compliance": [],
          "relatedurl": "",
          "description": "**IAM Access Analyzer** presence and status are evaluated per account and Region. An analyzer in `ACTIVE` state indicates continuous analysis of supported resources and IAM activity to identify external, internal, and unused access.",
          "remediation": {
            "code": {
              "cli": "aws accessanalyzer create-analyzer --analyzer-name example_resource --type ACCOUNT",
              "other": "1. In the AWS Console, open IAM\n2. Go to Access analyzer > Analyzer settings\n3. Confirm the desired Region\n4. Click Create analyzer\n5. Select Resource analysis - External access\n6. Set Name to \"example_resource\" and Zone of trust to \"Current account\"\n7. Click Create",
              "nativeiac": "```yaml\nResources:\n  example_resource:\n    Type: AWS::AccessAnalyzer::Analyzer  # This resource enables IAM Access Analyzer\n    Properties:\n      AnalyzerName: example_resource\n      Type: ACCOUNT  # This line fixes the security issue\n```",
              "terraform": "```hcl\nresource \"aws_accessanalyzer_analyzer\" \"example_resource\" {\n  analyzer_name = \"example_resource\"\n  type          = \"ACCOUNT\" # This line fixes the security issue\n}\n```"
            },
            "recommendation": {
              "url": "https://hub.prowler.com/check/accessanalyzer_enabled",
              "text": "Enable **IAM Access Analyzer** across all accounts and active Regions (*or organization-wide*). Operate on least privilege: continuously review findings, remove unintended access, and trim unused permissions. Use archive rules sparingly, integrate reviews into change/CI/CD workflows, and enforce separation of duties on policy changes."
            }
          },
          "servicename": "accessanalyzer",
          "checkaliases": [],
          "resourcetype": "Other",
          "resourcegroup": "security",
          "additionalurls": [
            "https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-manage-external.html",
            "https://aws.amazon.com/iam/access-analyzer/",
            "https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-getting-started.html",
            "https://docs.aws.amazon.com/access-analyzer/latest/APIReference/API_CreateAnalyzer.html",
            "https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html",
            "https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-create-external.html",
            "https://docs.aws.amazon.com/access-analyzer/latest/APIReference/Welcome.html",
            "https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-create-internal.html"
          ],
          "subservicename": "",
          "resourceidtemplate": ""
        },
        "categories": [
          "identity-access",
          "trust-boundaries"
        ],
        "resource_group": "security",
        "raw_result": {},
        "inserted_at": "2026-01-08T10:31:05.416673Z",
        "updated_at": "2026-01-08T10:31:05.416675Z",
        "first_seen_at": "2026-01-08T10:31:05.415317Z",
        "muted": false,
        "muted_reason": null
      },
      "relationships": {
        "scan": {
          "data": {
            "type": "scans",
            "id": "019b9d28-9fa5-7a6e-afee-f43f0131f7a6"
          }
        },
        "resources": {
          "meta": {
            "count": 1
          },
          "data": [
            {
              "type": "resources",
              "id": "cba12781-aec2-4866-b7c3-1f4fd834a5fd"
            }
          ]
        }
      },
      "links": {
        "self": "http://localhost:8080/api/v1/findings/019b9d28-f3c7-7d79-bd40-44758ededb27"
      }
    }

Resources:

{
      "type": "resources",
      "id": "cba12781-aec2-4866-b7c3-1f4fd834a5fd",
      "attributes": {
        "inserted_at": "2026-01-08T10:31:05.414231Z",
        "updated_at": "2026-01-08T10:31:05.414237Z",
        "uid": "arn:aws:accessanalyzer:ap-northeast-2:106908755756:analyzer/unknown",
        "name": "analyzer/unknown",
        "region": "ap-northeast-2",
        "service": "accessanalyzer",
        "tags": {},
        "failed_findings_count": 1,
        "metadata": "{\"arn\": \"arn:aws:accessanalyzer:ap-northeast-2:106908755756:analyzer/unknown\", \"name\": \"analyzer/unknown\", \"status\": \"NOT_AVAILABLE\", \"findings\": [], \"tags\": [], \"type\": \"\", \"region\": \"ap-northeast-2\"}",
        "details": "",
        "partition": "aws",
        "group": null,
        "type": "Other"
      },
      "relationships": {
        "provider": {
          "data": {
            "type": "providers",
            "id": "6d03e61c-a326-4f30-adbe-72d41beded59"
          }
        },
        "findings": {
          "data": [
            {
              "type": "findings",
              "id": "019b9d28-f3c7-7d79-bd40-44758ededb27"
            }
          ],
          "meta": {
            "count": 1
          }
        }
      },
      "links": {
        "self": "http://localhost:8080/api/v1/resources/cba12781-aec2-4866-b7c3-1f4fd834a5fd"
      }
    }

Changes made (rename resource_group → group)

Renamed field from resource_group to group for consistency with API naming conventions (singular for model fields, plural for metadata).

Files changed:

  • api/src/backend/api/models.py - Finding.group, ScanResourceGroupSummary.group
  • api/src/backend/api/migrations/0066_*.py - Updated to use group column name
  • api/src/backend/api/filters.py - filter[group], filter[group__in]
  • api/src/backend/api/v1/serializers.py - group field in responses
  • api/src/backend/api/v1/views.py - /api/v1/overviews/groups endpoint
  • api/src/backend/api/utils.py - groups in metadata
  • api/src/backend/tasks/jobs/scan.py - Fixed: added group to bulk_update fields
  • api/src/backend/tasks/jobs/backfill.py - Updated to use group
  • Tests updated accordingly

Bug fixed:
group: null in Resources was caused by group field missing from the bulk_update fields list in scan.py. The value was being assigned but never persisted to DB.

Manual testing:

  • ✅ GET /api/v1/findings → returns group field
  • ✅ GET /api/v1/resources → returns group field
  • ✅ GET /api/v1/findings?filter[group]=monitoring → works
  • ✅ GET /api/v1/findings?filter[group__in]=monitoring,security → works
  • ✅ GET /api/v1/overviews/groups → returns group aggregations
  • ✅ GET /api/v1/findings/metadata → returns groups (plural)

Copy link
Member

@jfagoagas jfagoagas left a comment

Choose a reason for hiding this comment

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

Thanks for the final changes @vicferpoy, as always:

@Alan-TheGentleman Alan-TheGentleman merged commit d8c1273 into master Jan 15, 2026
36 checks passed
@Alan-TheGentleman Alan-TheGentleman deleted the PROWLER-37-resource-inventory-component-api branch January 15, 2026 12:05
Alan-TheGentleman added a commit that referenced this pull request Jan 15, 2026
Co-authored-by: Alan Buscaglia <[email protected]>
Co-authored-by: Víctor Fernández Poyatos <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/api review-django-migrations This PR contains changes in Django migrations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants