Skip to content

BED-5784 - Search Asset Group Tags Endpoint #1648

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: main
Choose a base branch
from

Conversation

stephanieslamb
Copy link
Contributor

@stephanieslamb stephanieslamb commented Jul 3, 2025

Description

Adding an endpoint for the ability to search tags or labels, selectors and members

Motivation and Context

Resolves BED-5784

Why is this change required? What problem does it solve?
This change will allow searching on the privileged zones page whether on the tiers or labels page.

How Has This Been Tested?

New unit tests have been added for new functionality and existing unit tests pass. (will post screenshots)

Screenshots (optional):

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

Summary by CodeRabbit

  • New Features
    • Added a new API endpoint to search asset group tags, selectors, and members by name or object ID with results limited to 20 items each.
  • Documentation
    • Updated API documentation to include the new search endpoint and introduced a standardized schema for asset group tag members.
  • Bug Fixes
    • Enhanced error messages for invalid tag types and queries shorter than three characters.
  • Tests
    • Added extensive tests covering the new search endpoint and database selector retrieval.
  • Chores
    • Added Apache 2.0 license headers to multiple UI component files.
  • Style
    • Improved code formatting in test files for consistency.

@stephanieslamb stephanieslamb self-assigned this Jul 3, 2025
Copy link
Contributor

coderabbitai bot commented Jul 3, 2025

Walkthrough

A new POST endpoint /api/v2/asset-group-tags/search was introduced for searching asset group tags, selectors, and members by name or object ID. Supporting backend logic, request/response models, error handling, OpenAPI documentation, and database methods were added or updated. Tests were implemented to validate the new handler. Minor formatting and license header changes were also included.

Changes

File(s) / Path(s) Change Summary
cmd/api/src/api/registration/v2.go Registered new POST endpoint /api/v2/asset-group-tags/search in API router.
cmd/api/src/api/v2/assetgrouptags.go
cmd/api/src/api/error.go
Added handler SearchAssetGroupTags, request/response types, search logic, error messages, and constants for the new search endpoint.
cmd/api/src/api/v2/assetgrouptags_test.go Added comprehensive tests for SearchAssetGroupTags covering validation, errors, and successful cases.
cmd/api/src/database/assetgrouptags.go Added GetAssetGroupTagSelectors method and updated SQL query logic for tag selectors and tags.
cmd/api/src/database/assetgrouptags_test.go Added and renamed tests for GetAssetGroupTagSelectors to verify selector retrieval logic.
cmd/api/src/database/mocks/db.go Added mock methods for GetAssetGroupTagSelectors to support testing.
packages/go/openapi/doc/openapi.json
packages/go/openapi/src/openapi.yaml
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml
Documented the new search endpoint, updated response schemas to use a shared member schema, and referenced new schema files in OpenAPI specs.
packages/go/openapi/src/schemas/model.asset-group-tags-member.yaml Added new OpenAPI schema for asset group tag members.
cmd/api/src/database/migration/migrations/v8.0.0.sql Added index on asset_group_tag_selectors.name for improved search performance.
cmd/api/src/services/graphify/analysis_integration_test.go Minor formatting adjustments to variable assignments (whitespace only).
packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsComboBox.tsx
packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsListItem.tsx
packages/javascript/bh-shared-ui/src/components/ExploreTable/explore-table-utils.ts
Added Apache License 2.0 headers to several JavaScript/TypeScript files.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API
    participant DB
    participant GraphDB

    Client->>API: POST /api/v2/asset-group-tags/search {query, tag_type}
    API->>DB: GetAssetGroupTags (filter by tag_type)
    DB-->>API: AssetGroupTags
    API->>DB: GetAssetGroupTagSelectors (SQL filter, limit)
    DB-->>API: AssetGroupTagSelectors
    API->>GraphDB: Query nodes (name/object_id contains query, kind in tag kinds, limit)
    GraphDB-->>API: Graph nodes (members)
    API-->>Client: 200 OK {tags, selectors, members}
Loading

Suggested reviewers

  • superlinkx

Poem

In the warren of code, a new search appears,
For asset group tags—let’s all give three cheers!
With endpoints and schemas, the docs now align,
And queries return results right on time.
🐇 Hopping through data, so swift and so bright,
This rabbit declares: searching’s a delight!


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 084d972 and adea072.

📒 Files selected for processing (1)
  • cmd/api/src/api/v2/assetgrouptags_test.go (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
cmd/api/src/api/v2/assetgrouptags_test.go (4)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/upload/upload.go:70-84
Timestamp: 2025-05-27T17:28:37.963Z
Learning: The current pattern in upload.go of passing both io.ReadCloser and *os.File to validation functions creates unclear resource ownership and makes lifecycle management error-prone. The user identified this as a "logical mess" that should be refactored to separate validation concerns from file I/O management.
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2594-2594
Timestamp: 2025-06-17T22:37:36.389Z
Learning: In Go table-driven tests, there's a distinction between main test function parallelism and subtest parallelism. Main test functions can safely use t.Parallel() for performance benefits, but individual subtests within table-driven tests may need to run sequentially to avoid race conditions with mocks, deferred functions, or shared resources.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-analysis
  • GitHub Check: build-ui
  • GitHub Check: run-tests
🔇 Additional comments (4)
cmd/api/src/api/v2/assetgrouptags_test.go (4)

28-28: LGTM: Import addition is appropriate.

The strings package import is needed for the strings.NewReader() calls in the new test function.


2254-2279: LGTM: Well-structured test setup.

The test setup follows good practices with proper mock initialization, handler setup, and context creation. The use of a local WrappedResponse struct for JSON unmarshaling is appropriate for testing the response structure.


2280-2344: LGTM: Error test cases are well-implemented.

The error test cases properly cover malformed JSON, validation errors, and database errors. The require.Contains calls are correctly ordered (unlike the issues mentioned in past review comments). The test structure is consistent and follows good testing practices.


2346-2566: Excellent test coverage for success scenarios.

The success test cases comprehensively cover the search functionality:

  • Different tag types (Tier, Label, Owned)
  • Name-based searching with filtering
  • Object ID-based searching
  • Complete response structure validation

Each test case properly sets up mocks, validates the entire response structure, and uses type-safe JSON unmarshaling. This provides robust coverage of the new SearchAssetGroupTags endpoint functionality.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@stephanieslamb stephanieslamb added enhancement New feature or request api A pull request containing changes affecting the API code. labels Jul 3, 2025
@stephanieslamb stephanieslamb marked this pull request as ready for review July 8, 2025 18:07
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (7)
cmd/api/src/database/assetgrouptags_test.go (1)

1034-1067: Test coverage is minimal - consider adding more comprehensive test scenarios.

The new test function only covers the happy path with an empty filter. Consider adding test cases for:

  • Different filter conditions (similar to existing tests in the file)
  • Error handling scenarios
  • Edge cases like no results found
  • Verification of returned data structure and content

Here's an example of how to expand the test coverage:

 func TestDatabase_GetAssetGroupTagSelectors(t *testing.T) {
 	var (
 		dbInst        = integration.SetupDB(t)
 		testCtx       = context.Background()
 		isDefault     = false
 		allowDisable  = true
 		autoCertify   = null.BoolFrom(false)
 		test1Selector = model.AssetGroupTagSelector{
 			Name:        "test selector name",
 			Description: "test description",
 			Seeds: []model.SelectorSeed{
 				{Type: model.SelectorTypeObjectId, Value: "ObjectID1234"},
 			},
 		}
 		test2Selector = model.AssetGroupTagSelector{
 			Name:        "test2 selector name",
 			Description: "test2 description",
 			Seeds: []model.SelectorSeed{
 				{Type: model.SelectorTypeCypher, Value: "MATCH (n:User) RETURN n LIMIT 1;"},
 			},
 		}
 	)

 	_, err := dbInst.CreateAssetGroupTagSelector(testCtx, 1, model.User{}, test1Selector.Name, test1Selector.Description, isDefault, allowDisable, autoCertify, test1Selector.Seeds)
 	require.NoError(t, err)
 	_, err = dbInst.CreateAssetGroupTagSelector(testCtx, 1, model.User{}, test2Selector.Name, test2Selector.Description, isDefault, allowDisable, autoCertify, test2Selector.Seeds)
 	require.NoError(t, err)

 	t.Run("returns all selectors", func(t *testing.T) {
 		items, err := dbInst.GetAssetGroupTagSelectors(testCtx, model.SQLFilter{})
 		require.NoError(t, err)
 		require.GreaterOrEqual(t, len(items), 2)
+		
+		// Verify the returned selectors contain expected data
+		var foundTest1, foundTest2 bool
+		for _, item := range items {
+			if item.Name == test1Selector.Name {
+				foundTest1 = true
+				require.Equal(t, test1Selector.Description, item.Description)
+			}
+			if item.Name == test2Selector.Name {
+				foundTest2 = true
+				require.Equal(t, test2Selector.Description, item.Description)
+			}
+		}
+		require.True(t, foundTest1, "test1 selector not found")
+		require.True(t, foundTest2, "test2 selector not found")
 	})
+	
+	t.Run("filters by name", func(t *testing.T) {
+		items, err := dbInst.GetAssetGroupTagSelectors(testCtx, model.SQLFilter{
+			SQLString: "name LIKE ?",
+			Params:    []any{"%test2%"},
+		})
+		require.NoError(t, err)
+		require.GreaterOrEqual(t, len(items), 1)
+		
+		// Verify only test2 selector is returned
+		found := false
+		for _, item := range items {
+			if item.Name == test2Selector.Name {
+				found = true
+				break
+			}
+		}
+		require.True(t, found, "test2 selector not found in filtered results")
+	})
 }
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (2)

25-25: Remove trailing spaces.

YAML linting detected trailing spaces on these lines.

-    description: The request body for searching asset group tags. 
+    description: The request body for searching asset group tags.
-                  tags: 
+                  tags:
-                  members: 
+                  members:

Also applies to: 51-51, 59-59


70-74: Fix indentation for nested properties.

The indentation for name and properties fields is incorrect according to YAML standards.

                        name:
-                            type: string
+                          type: string
                        properties:
-                            type: object
-                            additionalProperties: true
+                          type: object
+                          additionalProperties: true
packages/go/openapi/doc/openapi.json (1)

6141-6145: OperationId style is inconsistent with existing endpoints

Most operations in this spec follow the verb-first SearchAssetGroupTags pattern (e.g. ListAssets, CreateUser). Consider renaming "AssetGroupTagSearch" to "SearchAssetGroupTags" for uniformity and easier client generation.

cmd/api/src/api/v2/assetgrouptags.go (2)

785-811: Simplify tag type filtering logic.

The current logic for grouping owned tags with labels is complex and could be more readable. Consider extracting this into a helper function.

+func shouldIncludeTag(tag model.AssetGroupTag, requestedType model.AssetGroupTagType) bool {
+	switch requestedType {
+	case model.AssetGroupTagTypeLabel:
+		return tag.Type == model.AssetGroupTagTypeLabel || tag.Type == model.AssetGroupTagTypeOwned
+	case model.AssetGroupTagTypeTier:
+		return tag.Type == model.AssetGroupTagTypeTier
+	default:
+		return false
+	}
+}

 for _, t := range tags {
-	isLabelType := t.Type == model.AssetGroupTagTypeLabel
-	isOwnedType := t.Type == model.AssetGroupTagTypeOwned
-
-	// group owned with labels
-	if reqBody.TagType == model.AssetGroupTagTypeLabel && (isLabelType || isOwnedType) {
-		kinds = append(kinds, t.ToKind())
-		if strings.Contains(strings.ToLower(t.Name), strings.ToLower(reqBody.Query)) {
-			matchedTags = append(matchedTags, t)
-
-			if len(matchedTags) >= assetGroupTagsSearchLimit {
-				break
-			}
-		}
-	}
-
-	if reqBody.TagType == model.AssetGroupTagTypeTier && t.Type == model.AssetGroupTagTypeTier {
+	if shouldIncludeTag(t, reqBody.TagType) {
 		kinds = append(kinds, t.ToKind())
 		if strings.Contains(strings.ToLower(t.Name), strings.ToLower(reqBody.Query)) {
 			matchedTags = append(matchedTags, t)
@@ -807,7 +789,6 @@
 				break
 			}
 		}
-	}
+	}
 }

748-830: Consider extracting search logic into separate methods.

The main handler function is quite long and handles multiple concerns. Consider extracting the search logic into separate methods for better maintainability.

Example structure:

func (s *Resources) SearchAssetGroupTags(response http.ResponseWriter, request *http.Request) {
    reqBody, err := s.parseSearchRequest(request)
    if err != nil {
        api.WriteErrorResponse(request.Context(), err, response)
        return
    }
    
    result, err := s.performSearch(request.Context(), reqBody, request.URL.Query())
    if err != nil {
        api.HandleDatabaseError(request, response, err)
        return
    }
    
    api.WriteBasicResponse(request.Context(), result, http.StatusOK, response)
}

func (s *Resources) parseSearchRequest(request *http.Request) (*AssetGroupTagSearchRequest, error) {
    // validation logic
}

func (s *Resources) performSearch(ctx context.Context, req *AssetGroupTagSearchRequest, queryParams url.Values) (*SearchAssetGroupTagsResponse, error) {
    // search logic
}
cmd/api/src/api/v2/assetgrouptags_test.go (1)

2256-2256: Add t.Parallel() for improved test performance.

Based on the codebase patterns, main test functions can safely use t.Parallel() for performance benefits.

 func TestDatabase_SearchAssetGroupTags(t *testing.T) {
+	t.Parallel()
 	var (
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dcfde37 and 0b8dc8c.

📒 Files selected for processing (10)
  • cmd/api/src/api/registration/v2.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags.go (3 hunks)
  • cmd/api/src/api/v2/assetgrouptags_test.go (2 hunks)
  • cmd/api/src/database/assetgrouptags.go (3 hunks)
  • cmd/api/src/database/assetgrouptags_test.go (2 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
  • cmd/api/src/model/assetgrouptags.go (1 hunks)
  • packages/go/openapi/doc/openapi.json (1 hunks)
  • packages/go/openapi/src/openapi.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
cmd/api/src/database/assetgrouptags_test.go (2)
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2594-2594
Timestamp: 2025-06-17T22:37:36.389Z
Learning: In Go table-driven tests, there's a distinction between main test function parallelism and subtest parallelism. Main test functions can safely use t.Parallel() for performance benefits, but individual subtests within table-driven tests may need to run sequentially to avoid race conditions with mocks, deferred functions, or shared resources.
cmd/api/src/api/v2/assetgrouptags_test.go (2)
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2594-2594
Timestamp: 2025-06-17T22:37:36.389Z
Learning: In Go table-driven tests, there's a distinction between main test function parallelism and subtest parallelism. Main test functions can safely use t.Parallel() for performance benefits, but individual subtests within table-driven tests may need to run sequentially to avoid race conditions with mocks, deferred functions, or shared resources.
🧬 Code Graph Analysis (5)
cmd/api/src/model/assetgrouptags.go (1)
packages/javascript/js-client-library/src/types.ts (3)
  • AssetGroupTagTypeTier (56-56)
  • AssetGroupTagTypeLabel (57-57)
  • AssetGroupTagTypeOwned (58-58)
cmd/api/src/api/registration/v2.go (1)
cmd/api/src/model/appcfg/flag.go (1)
  • FeatureTierManagement (42-42)
cmd/api/src/database/mocks/db.go (2)
cmd/api/src/model/filter.go (1)
  • SQLFilter (93-96)
cmd/api/src/model/assetgrouptags.go (1)
  • AssetGroupTagSelectors (191-191)
cmd/api/src/database/assetgrouptags_test.go (3)
cmd/api/src/test/integration/database.go (1)
  • SetupDB (57-63)
cmd/api/src/model/assetgrouptags.go (4)
  • AssetGroupTagSelector (193-209)
  • AssetGroupTagSelector (211-213)
  • SelectorSeed (170-174)
  • SelectorSeed (176-178)
cmd/api/src/model/filter.go (1)
  • SQLFilter (93-96)
cmd/api/src/database/assetgrouptags.go (4)
cmd/api/src/model/filter.go (1)
  • SQLFilter (93-96)
cmd/api/src/model/assetgrouptags.go (5)
  • AssetGroupTagSelectors (191-191)
  • AssetGroupTag (74-89)
  • AssetGroupTag (93-95)
  • AssetGroupTagSelector (193-209)
  • AssetGroupTagSelector (211-213)
cmd/api/src/database/helper.go (1)
  • CheckError (26-32)
cmd/api/src/database/db.go (1)
  • BloodhoundDB (176-179)
🪛 YAMLlint (1.37.1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml

[error] 25-25: trailing spaces

(trailing-spaces)


[error] 51-51: trailing spaces

(trailing-spaces)


[error] 59-59: trailing spaces

(trailing-spaces)


[warning] 71-71: wrong indentation: expected 26 but found 28

(indentation)


[warning] 73-73: wrong indentation: expected 26 but found 28

(indentation)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-tests
🔇 Additional comments (11)
cmd/api/src/model/assetgrouptags.go (1)

151-153: LGTM! Clean validation function implementation.

The IsValidTagType function is well-implemented with clear logic and purpose. It properly validates against the three defined constants and follows Go naming conventions.

cmd/api/src/api/registration/v2.go (1)

177-177: LGTM! Route registration follows established patterns.

The new search endpoint is properly registered with appropriate feature flag checks and permissions. The placement within the asset group management API section is logical and consistent with existing routes.

packages/go/openapi/src/openapi.yaml (1)

355-356: LGTM! OpenAPI specification properly updated.

The new search endpoint is correctly added to the OpenAPI specification with proper path structure and external file reference. The placement within the asset isolation section maintains logical organization.

cmd/api/src/database/mocks/db.go (1)

1164-1177: LGTM! Well-implemented mock method following GoMock conventions.

The new GetAssetGroupTagSelectors mock method and its corresponding recorder method are properly implemented following standard GoMock patterns. The method signature correctly matches the expected parameters (context.Context and model.SQLFilter) and return types (model.AssetGroupTagSelectors and error), which aligns with the search functionality requirements described in the PR objectives.

cmd/api/src/database/assetgrouptags_test.go (1)

833-833: Function rename looks good.

The renaming from TestDatabase_GetAssetGroupTagSelectors to TestDatabase_GetAssetGroupTagSelectorsBySelectorId better reflects the actual method being tested (GetAssetGroupTagSelectorBySelectorId).

packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1)

16-89: API specification structure looks comprehensive.

The endpoint definition includes:

  • Proper operation ID and summary
  • Clear request body schema with required fields
  • Well-structured response schema with nested objects
  • Standard error response references

The API design supports searching across tags, selectors, and members which aligns with the PR objectives.

cmd/api/src/database/assetgrouptags.go (2)

245-245: Good addition of explicit ordering.

Adding ORDER BY name ASC, id ASC ensures consistent and deterministic ordering of results, which is important for pagination and user experience.


712-731: Well-implemented method following existing patterns.

The implementation correctly uses parameterized queries and follows the established pattern for handling SQL filters. The ordering by name, asset_group_tag_id, and id ensures consistent results.

Note that this method includes disabled selectors (no disabled_at IS NULL filter), which differs from some other methods like GetAssetGroupTagSelectorCounts. Please verify this is intentional for the search functionality.

cmd/api/src/api/v2/assetgrouptags.go (3)

27-27: LGTM: Import addition is appropriate.

The strings import is correctly added to support case-insensitive string operations in the search functionality.


51-51: LGTM: Search limit constant is well-defined.

The assetGroupTagsSearchLimit constant provides a reasonable limit for search results and follows the existing naming convention.


737-746: LGTM: Type definitions are well-structured.

The request and response types are appropriately defined with clear field names and proper JSON tags.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (4)

25-25: Fix trailing spaces.

Remove trailing spaces as flagged by yamllint.

-    description: The request body for searching asset group tags. 
+    description: The request body for searching asset group tags.

51-51: Fix trailing spaces.

Remove trailing spaces as flagged by yamllint.

-                  tags: 
+                  tags:

59-59: Fix trailing spaces.

Remove trailing spaces as flagged by yamllint.

-                  members: 
+                  members:

71-73: Fix indentation.

The indentation for these properties is inconsistent. Adjust to match the expected 26-space indentation.

                        name:
-                            type: string
+                          type: string
                        properties:
-                            type: object
+                          type: object
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b8dc8c and f9e7471.

📒 Files selected for processing (3)
  • cmd/api/src/api/v2/assetgrouptags_test.go (2 hunks)
  • packages/go/openapi/doc/openapi.json (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/go/openapi/doc/openapi.json
🧰 Additional context used
🧠 Learnings (1)
cmd/api/src/api/v2/assetgrouptags_test.go (3)
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/upload/upload.go:70-84
Timestamp: 2025-05-27T17:28:37.963Z
Learning: The current pattern in upload.go of passing both io.ReadCloser and *os.File to validation functions creates unclear resource ownership and makes lifecycle management error-prone. The user identified this as a "logical mess" that should be refactored to separate validation concerns from file I/O management.
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2594-2594
Timestamp: 2025-06-17T22:37:36.389Z
Learning: In Go table-driven tests, there's a distinction between main test function parallelism and subtest parallelism. Main test functions can safely use t.Parallel() for performance benefits, but individual subtests within table-driven tests may need to run sequentially to avoid race conditions with mocks, deferred functions, or shared resources.
🪛 YAMLlint (1.37.1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml

[error] 25-25: trailing spaces

(trailing-spaces)


[error] 51-51: trailing spaces

(trailing-spaces)


[error] 59-59: trailing spaces

(trailing-spaces)


[warning] 71-71: wrong indentation: expected 26 but found 28

(indentation)


[warning] 73-73: wrong indentation: expected 26 but found 28

(indentation)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-tests
  • GitHub Check: build-ui
  • GitHub Check: run-analysis
🔇 Additional comments (2)
cmd/api/src/api/v2/assetgrouptags_test.go (2)

28-28: LGTM - Good addition of strings import.

The strings import is appropriately added to support the test implementation.


2256-2595: Excellent comprehensive test coverage.

The test function TestDatabase_SearchAssetGroupTags provides thorough coverage of the search endpoint including:

  • Input validation (malformed JSON, invalid tag types, empty queries)
  • Database error scenarios
  • Parameter parsing errors
  • Multiple successful search scenarios by name, type, and object ID
  • Proper response structure validation

The test structure is well-organized with clear test case names and proper setup/teardown using mocks.

Comment on lines 151 to 153
func IsValidTagType(tagType AssetGroupTagType) bool {
return tagType == AssetGroupTagTypeTier || tagType == AssetGroupTagTypeLabel || tagType == AssetGroupTagTypeOwned
}
Copy link
Member

Choose a reason for hiding this comment

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

Maybe this is a question for @mistahj67 but now that we have 0 for hygiene should that be included here too?

Copy link
Member

Choose a reason for hiding this comment

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

I guess for the particular usage here this is what we want, but just based on the method name I'm still wondering if we should handle 0 somehow.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd likely nix this entirely in favor of leveraging the ToType() method and kicking out if it's unknown. Findings arent a factor for this endpoint, it's just the search for tags / selectors / members

Comment on lines 151 to 153
func IsValidTagType(tagType AssetGroupTagType) bool {
return tagType == AssetGroupTagTypeTier || tagType == AssetGroupTagTypeLabel || tagType == AssetGroupTagTypeOwned
}
Copy link
Member

Choose a reason for hiding this comment

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

I guess for the particular usage here this is what we want, but just based on the method name I'm still wondering if we should handle 0 somehow.

api.WriteErrorResponse(request.Context(), ErrBadQueryParameter(request, model.PaginationQueryParameterSkip, err), response)
} else {
var (
kinds []graph.Kind
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
kinds []graph.Kind
kinds graph.Kinds

This will also allow you to use kinds.Add(...) instead of append if you want.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1)

25-25: Fix trailing spaces to comply with YAML formatting standards.

The static analysis tool identified trailing spaces that should be removed for consistent formatting.

-  description: The request body for searching asset group tags. 
+  description: The request body for searching asset group tags.
-                  tags: 
+                  tags:
-                  members: 
+                  members:

Also applies to: 51-51, 59-59

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9e7471 and 8626a6c.

📒 Files selected for processing (10)
  • cmd/api/src/api/error.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags.go (3 hunks)
  • cmd/api/src/api/v2/assetgrouptags_test.go (2 hunks)
  • cmd/api/src/database/assetgrouptags.go (3 hunks)
  • packages/go/openapi/doc/openapi.json (5 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.id.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.selectors.id.members.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1 hunks)
  • packages/go/openapi/src/schemas/model.asset-group-tags-member.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.selectors.id.members.yaml
  • cmd/api/src/api/error.go
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.yaml
  • packages/go/openapi/src/schemas/model.asset-group-tags-member.yaml
🚧 Files skipped from review as they are similar to previous changes (2)
  • cmd/api/src/api/v2/assetgrouptags.go
  • cmd/api/src/database/assetgrouptags.go
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.id.yaml (1)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
cmd/api/src/api/v2/assetgrouptags_test.go (4)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/upload/upload.go:70-84
Timestamp: 2025-05-27T17:28:37.963Z
Learning: The current pattern in upload.go of passing both io.ReadCloser and *os.File to validation functions creates unclear resource ownership and makes lifecycle management error-prone. The user identified this as a "logical mess" that should be refactored to separate validation concerns from file I/O management.
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2594-2594
Timestamp: 2025-06-17T22:37:36.389Z
Learning: In Go table-driven tests, there's a distinction between main test function parallelism and subtest parallelism. Main test functions can safely use t.Parallel() for performance benefits, but individual subtests within table-driven tests may need to run sequentially to avoid race conditions with mocks, deferred functions, or shared resources.
packages/go/openapi/doc/openapi.json (1)

undefined

<retrieved_learning>
Learnt from: mistahj67
PR: #1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete kinds array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
</retrieved_learning>

packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
🧬 Code Graph Analysis (1)
cmd/api/src/api/v2/assetgrouptags_test.go (9)
cmd/api/src/database/mocks/db.go (1)
  • NewMockDatabase (55-59)
cmd/api/src/queries/mocks/graph.go (1)
  • NewMockGraph (53-57)
cmd/api/src/api/v2/model.go (1)
  • Resources (102-115)
cmd/api/src/api/v2/assetgrouptags.go (2)
  • SearchAssetGroupTagsResponse (737-741)
  • AssetGroupMember (528-536)
cmd/api/src/ctx/ctx.go (1)
  • Set (88-90)
packages/go/headers/headers.go (1)
  • ContentType (75-75)
packages/go/mediatypes/application.go (1)
  • ApplicationJson (214-214)
cmd/api/src/model/assetgrouptags.go (2)
  • AssetGroupTags (91-91)
  • AssetGroupTagSelectors (191-191)
packages/go/graphschema/common/common.go (3)
  • Name (53-53)
  • ObjectID (52-52)
  • PrimaryKind (70-70)
🪛 YAMLlint (1.37.1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml

[error] 25-25: trailing spaces

(trailing-spaces)


[error] 51-51: trailing spaces

(trailing-spaces)


[error] 59-59: trailing spaces

(trailing-spaces)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: build-ui
  • GitHub Check: run-tests
  • GitHub Check: run-analysis
🔇 Additional comments (7)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.id.yaml (1)

54-61: Excellent schema refactoring for reusability.

The change from inline schema definition to allOf composition with a shared reference promotes consistency and maintainability across the API. This approach correctly extends the base member schema while adding the specific selectors property.

cmd/api/src/api/v2/assetgrouptags_test.go (5)

2256-2277: Comprehensive test coverage for the new search endpoint.

The test function provides excellent coverage of the SearchAssetGroupTags functionality, including error scenarios, input validation, and success cases across different tag types. The test setup with proper mocking and context creation follows established patterns in the codebase.


2282-2318: Well-structured error case testing.

The error case tests properly validate input validation scenarios including malformed JSON, invalid tag types, and query length validation. The test cases appropriately use mock expectations and validate the expected HTTP status codes and error messages.


2375-2428: Thorough success scenario testing with proper response validation.

The success test cases comprehensively validate the search functionality across different tag types (tier, label) and search criteria (name, object ID). The test properly validates the response structure and content, ensuring the API returns the expected data format.


2485-2538: Good test coverage for mixed tag type scenarios.

This test case effectively validates the behavior when searching across multiple tag types, ensuring the API correctly handles queries that match tags of different types (label and owned). The test validates that the response includes all matching tags while filtering selectors appropriately.


2540-2594: Proper validation of object ID search functionality.

The test correctly validates the search behavior when querying by object ID, ensuring that only members with matching object IDs are returned while tags and selectors remain empty when no name matches occur. This demonstrates proper separation of search criteria handling.

packages/go/openapi/doc/openapi.json (1)

5350-5354: 👍 Reference consolidation looks good

Replacing the inline member definitions with a shared $ref improves consistency and reduces spec drift. Nice clean-up!

Also applies to: 5523-5527

Comment on lines +6110 to +6127
"properties": {
"query": {
"type": "string"
},
"tag_type": {
"type": "integer"
}
},
"required": [
"query",
"tag_type"
]
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add basic validation for query and constrain tag_type to known values

The handler rejects queries shorter than 3 characters and validates tag_type against a finite enum. Encoding those rules in the schema prevents round-trips with immediately‐invalid payloads.

                 "properties": {
                   "query": {
-                    "type": "string"
+                    "type": "string",
+                    "minLength": 3,
+                    "description": "Case-insensitive substring to match; must be ≥ 3 characters."
                   },
                   "tag_type": {
-                    "type": "integer"
+                    "type": "integer",
+                    "enum": [0, 1],                     // 0 = selector tag, 1 = member tag
+                    "description": "Asset-group tag type.  See model.AssetGroupTagType."
                   }
                 },
                 "required": [
                   "query",
                   "tag_type"
                 ]

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/go/openapi/doc/openapi.json around lines 6110 to 6121, the schema
for the "query" and "tag_type" properties lacks validation constraints. Add a
"minLength" of 3 to the "query" property to enforce the minimum character
length, and define an "enum" for "tag_type" listing all valid integer values to
restrict it to known options. This will ensure the schema validates inputs
consistently with the handler logic.

Comment on lines +4850 to 4863
"allOf": [
{
"$ref": "#/components/schemas/model.asset-group-tags-member"
},
"properties": {
{
"type": "object",
"additionalProperties": true
},
"source": {
"type": "integer"
},
"selectors": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-selector-response"
"properties": {
"selectors": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-selector-response"
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Mark selectors as a required property inside the composed member object

selectors is not optional in the actual response – every call that returns a member object includes the matched selectors. Add a required array to the second allOf object so schema-driven clients don’t think the field might be omitted.

                             "properties": {
                               "selectors": {
                                 "type": "array",
                                 "items": {
                                   "$ref": "#/components/schemas/model.asset-group-tags-selector-response"
                                 }
                               }
+                              },
+                              "required": [
+                                "selectors"
+                              ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"allOf": [
{
"$ref": "#/components/schemas/model.asset-group-tags-member"
},
"properties": {
{
"type": "object",
"additionalProperties": true
},
"source": {
"type": "integer"
},
"selectors": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-selector-response"
"properties": {
"selectors": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-selector-response"
}
}
}
"allOf": [
{
"$ref": "#/components/schemas/model.asset-group-tags-member"
},
{
"type": "object",
"properties": {
"selectors": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-selector-response"
}
}
},
"required": [
"selectors"
]
}
🤖 Prompt for AI Agents
In packages/go/openapi/doc/openapi.json around lines 4850 to 4863, the
`selectors` property inside the composed `member` object is missing from the
`required` array, making it appear optional. Add a `required` array to the
second `allOf` object and include `"selectors"` in it to indicate that this
property is mandatory in the schema.

Comment on lines +6134 to +6161
"data": {
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tag"
}
},
"selectors": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-selector-response"
}
},
"members": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-member"
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Define required fields for the success payload

Clients can’t rely on tags, selectors, or members being present because they’re not marked as required. They are always returned (even if empty arrays), so mark them as mandatory.

                       "properties": {
                         "tags": {
                           "type": "array",
                           "items": {
                             "$ref": "#/components/schemas/model.asset-group-tag"
                           }
                         },
                         "selectors": {
                           "type": "array",
                           "items": {
                             "$ref": "#/components/schemas/model.asset-group-tags-selector-response"
                           }
                         },
                         "members": {
                           "type": "array",
                           "items": {
                             "$ref": "#/components/schemas/model.asset-group-tags-member"
                           }
                         }
+                      },
+                      "required": [
+                        "tags",
+                        "selectors",
+                        "members"
                       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"data": {
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tag"
}
},
"selectors": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-selector-response"
}
},
"members": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-member"
}
}
}
"data": {
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tag"
}
},
"selectors": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-selector-response"
}
},
"members": {
"type": "array",
"items": {
"$ref": "#/components/schemas/model.asset-group-tags-member"
}
}
},
"required": [
"tags",
"selectors",
"members"
]
}
🤖 Prompt for AI Agents
In packages/go/openapi/doc/openapi.json around lines 6134 to 6155, the
properties "tags", "selectors", and "members" inside the "data" object are not
marked as required, but they are always returned even if empty. To fix this, add
a "required" array listing these three properties to the "data" object schema to
indicate they are mandatory fields.

Comment on lines +17723 to +17680
"model.asset-group-tags-member": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"object_id": {
"type": "string"
},
"primary_kind": {
"type": "string"
},
"name": {
"type": "string"
},
"properties": {
"type": "object",
"additionalProperties": true
},
"source": {
"type": "integer"
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Schema completeness: list required member fields and lock down extra properties

id, object_id, primary_kind, and name are always present, and unknown keys should be forbidden for forward-compatibility.

       "model.asset-group-tags-member": {
         "type": "object",
+        "additionalProperties": false,
         "properties": {
           "id": {
             "type": "integer"
           },
           "object_id": {
             "type": "string"
           },
           "primary_kind": {
             "type": "string"
           },
           "name": {
             "type": "string"
           },
           "properties": {
             "type": "object",
             "additionalProperties": true
           },
           "source": {
             "type": "integer"
           }
-        }
+        },
+        "required": [
+          "id",
+          "object_id",
+          "primary_kind",
+          "name"
+        ]
       },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"model.asset-group-tags-member": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"object_id": {
"type": "string"
},
"primary_kind": {
"type": "string"
},
"name": {
"type": "string"
},
"properties": {
"type": "object",
"additionalProperties": true
},
"source": {
"type": "integer"
}
}
"model.asset-group-tags-member": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer"
},
"object_id": {
"type": "string"
},
"primary_kind": {
"type": "string"
},
"name": {
"type": "string"
},
"properties": {
"type": "object",
"additionalProperties": true
},
"source": {
"type": "integer"
}
},
"required": [
"id",
"object_id",
"primary_kind",
"name"
]
},
🤖 Prompt for AI Agents
In packages/go/openapi/doc/openapi.json around lines 17723 to 17745, the schema
for "model.asset-group-tags-member" lacks a "required" array listing mandatory
fields and does not forbid additional unknown properties. Add a "required" field
listing "id", "object_id", "primary_kind", and "name" to enforce their presence,
and set "additionalProperties" to false to prevent unknown keys for better
forward-compatibility.

Copy link
Contributor

@mistahj67 mistahj67 left a comment

Choose a reason for hiding this comment

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

Just added the PR block because there was another approval and I didn't want these to be missed! Let me know if / when you want to pair to knock out feedback!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1)

25-25: Fix trailing spaces identified by static analysis.

Remove trailing spaces on lines 25, 51, and 59 to maintain consistent formatting.

-    description: The request body for searching asset group tags. 
+    description: The request body for searching asset group tags.
-                  tags: 
+                  tags:
-                  members: 
+                  members:

Also applies to: 51-51, 59-59

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c67b7c2 and 13d6723.

📒 Files selected for processing (19)
  • cmd/api/src/api/error.go (1 hunks)
  • cmd/api/src/api/registration/v2.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags.go (3 hunks)
  • cmd/api/src/api/v2/assetgrouptags_test.go (2 hunks)
  • cmd/api/src/database/assetgrouptags.go (4 hunks)
  • cmd/api/src/database/assetgrouptags_test.go (2 hunks)
  • cmd/api/src/database/migration/migrations/v8.0.0.sql (1 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
  • cmd/api/src/services/graphify/analysis_integration_test.go (4 hunks)
  • packages/go/openapi/doc/openapi.json (5 hunks)
  • packages/go/openapi/src/openapi.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.id.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.selectors.id.members.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1 hunks)
  • packages/go/openapi/src/schemas/model.asset-group-tags-member.yaml (1 hunks)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsComboBox.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsListItem.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/explore-table-utils.ts (1 hunks)
✅ Files skipped from review due to trivial changes (6)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsListItem.tsx
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/explore-table-utils.ts
  • cmd/api/src/database/migration/migrations/v8.0.0.sql
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsComboBox.tsx
  • packages/go/openapi/src/openapi.yaml
  • cmd/api/src/services/graphify/analysis_integration_test.go
🚧 Files skipped from review as they are similar to previous changes (11)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.yaml
  • cmd/api/src/api/registration/v2.go
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.selectors.id.members.yaml
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.id.yaml
  • packages/go/openapi/src/schemas/model.asset-group-tags-member.yaml
  • cmd/api/src/database/mocks/db.go
  • cmd/api/src/database/assetgrouptags_test.go
  • cmd/api/src/api/error.go
  • cmd/api/src/api/v2/assetgrouptags.go
  • packages/go/openapi/doc/openapi.json
  • cmd/api/src/database/assetgrouptags.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
cmd/api/src/api/v2/assetgrouptags_test.go (4)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/upload/upload.go:70-84
Timestamp: 2025-05-27T17:28:37.963Z
Learning: The current pattern in upload.go of passing both io.ReadCloser and *os.File to validation functions creates unclear resource ownership and makes lifecycle management error-prone. The user identified this as a "logical mess" that should be refactored to separate validation concerns from file I/O management.
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2594-2594
Timestamp: 2025-06-17T22:37:36.389Z
Learning: In Go table-driven tests, there's a distinction between main test function parallelism and subtest parallelism. Main test functions can safely use t.Parallel() for performance benefits, but individual subtests within table-driven tests may need to run sequentially to avoid race conditions with mocks, deferred functions, or shared resources.
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
🧬 Code Graph Analysis (1)
cmd/api/src/api/v2/assetgrouptags_test.go (2)
cmd/api/src/model/assetgrouptags.go (2)
  • AssetGroupTags (92-92)
  • AssetGroupTagSelectors (188-188)
packages/go/graphschema/common/common.go (1)
  • Name (53-53)
🪛 YAMLlint (1.37.1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml

[error] 25-25: trailing spaces

(trailing-spaces)


[error] 51-51: trailing spaces

(trailing-spaces)


[error] 59-59: trailing spaces

(trailing-spaces)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-tests
  • GitHub Check: build-ui
  • GitHub Check: run-analysis
🔇 Additional comments (2)
cmd/api/src/api/v2/assetgrouptags_test.go (2)

2254-2593: Comprehensive test coverage for SearchAssetGroupTags endpoint.

This test function provides excellent coverage of the new search functionality, including:

  • Input validation scenarios (malformed JSON, invalid tag type, short queries)
  • Database error handling for both tags and selectors
  • Query parameter parsing validation
  • Success scenarios for different search patterns (by name, by object ID, different tag types)

The test structure follows Go best practices with clear subtest organization and appropriate mock expectations. The response validation thoroughly checks both HTTP status codes and JSON response content.


28-28: Import addition looks correct.

The addition of the strings import is appropriate for the new test functions that use strings.NewReader.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1)

25-25: Remove trailing spaces.

Static analysis detected trailing spaces on these lines that should be removed for consistency.

-    description: The request body for searching asset group tags. 
+    description: The request body for searching asset group tags.
-                  tags: 
+                  tags:
-                  members: 
+                  members:

Also applies to: 51-51, 59-59

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebb7008 and 76e597f.

📒 Files selected for processing (20)
  • cmd/api/src/api/error.go (1 hunks)
  • cmd/api/src/api/registration/v2.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags.go (3 hunks)
  • cmd/api/src/api/v2/assetgrouptags_test.go (2 hunks)
  • cmd/api/src/database/assetgrouptags.go (4 hunks)
  • cmd/api/src/database/assetgrouptags_test.go (2 hunks)
  • cmd/api/src/database/migration/migrations/v8.0.0.sql (1 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
  • cmd/api/src/services/graphify/analysis_integration_test.go (4 hunks)
  • packages/go/openapi/doc/openapi.json (5 hunks)
  • packages/go/openapi/src/openapi.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.id.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.selectors.id.members.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1 hunks)
  • packages/go/openapi/src/schemas/model.asset-group-tags-member.yaml (1 hunks)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsComboBox.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsListItem.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/explore-table-utils.ts (1 hunks)
  • packages/javascript/bh-shared-ui/src/graphSchema.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • packages/javascript/bh-shared-ui/src/graphSchema.ts
✅ Files skipped from review due to trivial changes (3)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsListItem.tsx
  • cmd/api/src/services/graphify/analysis_integration_test.go
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsComboBox.tsx
🚧 Files skipped from review as they are similar to previous changes (14)
  • cmd/api/src/database/migration/migrations/v8.0.0.sql
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/explore-table-utils.ts
  • cmd/api/src/api/registration/v2.go
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.selectors.id.members.yaml
  • packages/go/openapi/src/openapi.yaml
  • cmd/api/src/api/error.go
  • cmd/api/src/database/assetgrouptags_test.go
  • packages/go/openapi/doc/openapi.json
  • cmd/api/src/api/v2/assetgrouptags.go
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.yaml
  • cmd/api/src/database/assetgrouptags.go
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.id.yaml
  • packages/go/openapi/src/schemas/model.asset-group-tags-member.yaml
  • cmd/api/src/database/mocks/db.go
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
cmd/api/src/api/v2/assetgrouptags_test.go (4)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/job/jobs_test.go:19-143
Timestamp: 2025-05-27T16:58:33.295Z
Learning: Tests in cmd/api/src/services/job/jobs_test.go have been found to be flaky in the past and are due for rewrite. They should be skipped with t.Skip() until they can be properly rewritten.
Learnt from: superlinkx
PR: SpecterOps/BloodHound#1503
File: cmd/api/src/services/upload/upload.go:70-84
Timestamp: 2025-05-27T17:28:37.963Z
Learning: The current pattern in upload.go of passing both io.ReadCloser and *os.File to validation functions creates unclear resource ownership and makes lifecycle management error-prone. The user identified this as a "logical mess" that should be refactored to separate validation concerns from file I/O management.
Learnt from: LawsonWillard
PR: SpecterOps/BloodHound#1595
File: cmd/api/src/api/v2/saved_queries_test.go:2594-2594
Timestamp: 2025-06-17T22:37:36.389Z
Learning: In Go table-driven tests, there's a distinction between main test function parallelism and subtest parallelism. Main test functions can safely use t.Parallel() for performance benefits, but individual subtests within table-driven tests may need to run sequentially to avoid race conditions with mocks, deferred functions, or shared resources.
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
🪛 YAMLlint (1.37.1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml

[error] 25-25: trailing spaces

(trailing-spaces)


[error] 51-51: trailing spaces

(trailing-spaces)


[error] 59-59: trailing spaces

(trailing-spaces)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-analysis
  • GitHub Check: build-ui
  • GitHub Check: run-tests
🔇 Additional comments (4)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (3)

16-39: Well-structured OpenAPI endpoint definition.

The endpoint definition is complete with appropriate operationId, summary, description, and request schema. The required fields correctly match the defined properties.


40-62: Good use of schema references for response structure.

The response schema appropriately uses $ref to reference reusable schemas, promoting consistency across the API while maintaining clear structure.


63-74: Complete error response coverage.

The endpoint correctly defines all standard error responses with appropriate HTTP status codes and references to reusable error schemas.

cmd/api/src/api/v2/assetgrouptags_test.go (1)

2254-2593: Excellent comprehensive test coverage for the search endpoint.

This test function provides thorough coverage of the SearchAssetGroupTags handler with well-structured test cases covering:

  • Input validation errors (malformed JSON, invalid tag type, short query)
  • Database error scenarios
  • Parameter parsing errors
  • Multiple success scenarios with different tag types
  • Search by both name and object ID

The test properly uses mocks, follows Go testing conventions, and consistently uses the endpoint variable as requested in previous feedback. The assertions are appropriate and the test data is realistic.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1)

18-23: Consider widening the summary

The description correctly mentions selectors & members, but the summary only says “Search Asset Group Tags”. To avoid confusion in API docs, maybe expand to “Search Asset Group Tags, Selectors, and Members”.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 76e597f and 084d972.

📒 Files selected for processing (19)
  • cmd/api/src/api/error.go (1 hunks)
  • cmd/api/src/api/registration/v2.go (1 hunks)
  • cmd/api/src/api/v2/assetgrouptags.go (3 hunks)
  • cmd/api/src/api/v2/assetgrouptags_test.go (2 hunks)
  • cmd/api/src/database/assetgrouptags.go (4 hunks)
  • cmd/api/src/database/assetgrouptags_test.go (2 hunks)
  • cmd/api/src/database/migration/migrations/v8.0.0.sql (1 hunks)
  • cmd/api/src/database/mocks/db.go (1 hunks)
  • cmd/api/src/services/graphify/analysis_integration_test.go (4 hunks)
  • packages/go/openapi/doc/openapi.json (5 hunks)
  • packages/go/openapi/src/openapi.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.id.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.selectors.id.members.yaml (1 hunks)
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1 hunks)
  • packages/go/openapi/src/schemas/model.asset-group-tags-member.yaml (1 hunks)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsComboBox.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsListItem.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/explore-table-utils.ts (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsComboBox.tsx
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/ManageColumnsComboBox/ManageColumnsListItem.tsx
  • cmd/api/src/services/graphify/analysis_integration_test.go
  • packages/go/openapi/src/openapi.yaml
🚧 Files skipped from review as they are similar to previous changes (14)
  • packages/javascript/bh-shared-ui/src/components/ExploreTable/explore-table-utils.ts
  • cmd/api/src/database/migration/migrations/v8.0.0.sql
  • cmd/api/src/api/registration/v2.go
  • cmd/api/src/database/mocks/db.go
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.selectors.id.members.yaml
  • cmd/api/src/api/error.go
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.id.yaml
  • packages/go/openapi/src/schemas/model.asset-group-tags-member.yaml
  • cmd/api/src/database/assetgrouptags_test.go
  • packages/go/openapi/src/paths/asset-isolation.asset-group-tags.id.members.yaml
  • cmd/api/src/api/v2/assetgrouptags_test.go
  • cmd/api/src/database/assetgrouptags.go
  • packages/go/openapi/doc/openapi.json
  • cmd/api/src/api/v2/assetgrouptags.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
🪛 YAMLlint (1.37.1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml

[error] 25-25: trailing spaces

(trailing-spaces)


[error] 51-51: trailing spaces

(trailing-spaces)


[error] 59-59: trailing spaces

(trailing-spaces)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-analysis
  • GitHub Check: run-tests
  • GitHub Check: build-ui
🔇 Additional comments (1)
packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml (1)

17-23: Align operation naming with existing conventions

Other endpoints use SearchAssetGroupTags (verb first) rather than AssetGroupTagSearch. Renaming keeps generated client methods predictable:

-  operationId: AssetGroupTagSearch
+  operationId: SearchAssetGroupTags

Comment on lines +24 to +38
requestBody:
description: The request body for searching asset group tags.
required: true
content:
application/json:
schema:
type: object
properties:
query:
type: string
tag_type:
type: integer
required:
- query
- tag_type
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Strengthen request schema ‒ add validation & clean up lint errors

  1. query
    • No minLength → empty strings will pass validation and execute a DB search.
  2. tag_type
    • Bare integer gives no clue what values are acceptable. An enum (or $ref to a shared TagType schema) prevents invalid numbers.
  3. Trailing spaces on lines 25, 37 → YAML-lint fails CI.
     application/json:
       schema:
         type: object
         properties:
           query:
             type: string
+            description: The case-insensitive substring to match.
+            minLength: 1
           tag_type:
-            type: integer
+            description: Asset-group tag category to limit the search.
+            type: integer
+            enum: [1, 2]            # ← replace with correct set or `$ref`
         required:
           - query
           - tag_type
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
requestBody:
description: The request body for searching asset group tags.
required: true
content:
application/json:
schema:
type: object
properties:
query:
type: string
tag_type:
type: integer
required:
- query
- tag_type
requestBody:
description: The request body for searching asset group tags.
required: true
content:
application/json:
schema:
type: object
properties:
query:
type: string
description: The case-insensitive substring to match.
minLength: 1
tag_type:
description: Asset-group tag category to limit the search.
type: integer
enum: [1, 2] # ← replace with correct set or `$ref`
required:
- query
- tag_type
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 25-25: trailing spaces

(trailing-spaces)

🤖 Prompt for AI Agents
In packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml
lines 24 to 38, enhance the requestBody schema by adding a minLength constraint
to the query property to prevent empty strings from passing validation. For
tag_type, replace the bare integer type with an enum or a $ref to a shared
TagType schema to restrict acceptable values and prevent invalid inputs. Also,
remove trailing spaces on lines 25 and 37 to fix YAML lint errors and ensure CI
passes.

Comment on lines +40 to +62
responses:
200:
description: OK
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
tags:
type: array
items:
$ref: './../schemas/model.asset-group-tag.yaml'
selectors:
type: array
items:
$ref: './../schemas/model.asset-group-tags-selector-response.yaml'
members:
type: array
items:
$ref: './../schemas/model.asset-group-tags-member.yaml'
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Response arrays should declare minItems: 0 and remove trailing spaces

OpenAPI generators treat absent minItems as “must have ≥1 item”, which breaks typed clients when the search yields no matches. Add minItems: 0 (or set arrays as nullable) so an empty search is valid. Also drop the trailing spaces on lines 51 & 59.

                 properties:
                   tags: 
                     type: array
+                    minItems: 0
                     items:
                       $ref: './../schemas/model.asset-group-tag.yaml'
                   selectors:
                     type: array
+                    minItems: 0
                     items:
                       $ref: './../schemas/model.asset-group-tags-selector-response.yaml'
                   members: 
                     type: array
+                    minItems: 0
                     items:
                       $ref: './../schemas/model.asset-group-tags-member.yaml'
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 51-51: trailing spaces

(trailing-spaces)


[error] 59-59: trailing spaces

(trailing-spaces)

🤖 Prompt for AI Agents
In packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml
between lines 40 and 62, the response arrays for tags, selectors, and members
lack the minItems property, causing generated clients to expect at least one
item. Add minItems: 0 to each array definition to allow empty arrays for no
search results. Also, remove any trailing spaces on lines 51 and 59 to clean up
the YAML formatting.

Copy link
Contributor

@mistahj67 mistahj67 left a comment

Choose a reason for hiding this comment

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

Code LGTM but as I implemented feedback, I'll wait to merge until another set of 👀

Approving to remove requested changes block

@mistahj67 mistahj67 requested a review from computator July 17, 2025 00:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api A pull request containing changes affecting the API code. enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants