Skip to content

Conversation

@sirisjo
Copy link
Contributor

@sirisjo sirisjo commented Jan 10, 2026

Do not merge until after RC

Description

  • This change adds allows users to find the shortest path between 2 OpenGraph nodes, using the shortest-path endpoint.
  • It also adds a new optional parameter, onlyTraversable, to the endpoint. If this parameter is included, the resulting path will contain its search to only traversable edges. Users will no longer have to pass in a hard-coded list of all the traversable kinds to relationship_kinds 🎉

NOTE: this work is gated behind a feature-flag, AND is dependent on BED-6723 to populate edge kind data in the schema-edge-kinds table. This endpoint will not return opengraph data if the opengraph edge kinds are not present in the schema-edge-kinds table. You can manually insert your schema edge kinds in the table if you want to play around with the endpoint with opengraph data in the meantime.

Motivation and Context

Resolves BED-6718

This addition will enable users to search for paths between opengraph nodes just like any other AD/AZ node.

How Has This Been Tested?

API tests and graph integration tests added.

Tested locally with some OpenGraph Nodes.

Screenshot 2026-01-09 at 16 17 18

Types of changes

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

Checklist:

Summary by CodeRabbit

  • New Features

    • Added only_traversable query parameter to the shortest-path endpoint and OpenGraph-backed path search.
  • Documentation

    • API docs updated to clarify relationship_kinds validation and only_traversable behavior.
  • Data

    • Updated OpenGraph test fixture graph data and edge relationships.
  • Tests

    • Expanded pathfinding tests to cover traversable-only filtering, OpenGraph on/off, and related error cases.

✏️ Tip: You can customize this high-level summary in your review settings.

@sirisjo sirisjo self-assigned this Jan 10, 2026
@sirisjo sirisjo added enhancement New feature or request api A pull request containing changes affecting the API code. labels Jan 10, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 10, 2026

Walkthrough

Adds an only_traversable query parameter and OpenGraph-aware pathfinding: new request-scoped context usage, traversable-kind filtering, OpenGraph node-fetch fallback, new graph query methods, fixtures/mocks, OpenAPI/schema updates, and expanded tests.

Changes

Cohort / File(s) Summary
Constants
cmd/api/src/api/constant.go
Added QueryParameterIncludeOnlyTraversableKinds constant and realigned existing query parameter constants.
API Pathfinding Core
cmd/api/src/api/v2/pathfinding.go
Added context usage; changed parseRelationshipKindsParam signature/validation; added kindIsValidBuiltIn, createRelationshipKindFilterCriteria; added getAllShortestPaths and getAllShortestPathsWithOpenGraph; wired only_traversable parsing and feature-flag branching in GetShortestPath.
API Tests
cmd/api/src/api/v2/pathfinding_internal_test.go, cmd/api/src/api/v2/pathfinding_test.go
Extended tests for traversable-only behavior and OpenGraph-enabled/disabled flows; updated test scaffolding to include DB/mocks and feature-flag permutations.
Query/Graph Layer
cmd/api/src/queries/graph.go, cmd/api/src/queries/mocks/graph.go
Introduced getAllShortestPathsInternal with pluggable node fetcher; GetAllShortestPaths delegates to it; new GetAllShortestPathsWithOpenGraph added; mocks extended for new method.
Integration Tests & Fixtures
cmd/api/src/queries/graph_integration_test.go, cmd/api/src/queries/fixtures/OpenGraphJSON/raw/base.json
Updated fixture node IDs and added top-level edges; added TestGetAllShortestPathsWithOpenGraph integration cases.
Analysis Layer
packages/go/analysis/analysis.go
Added openGraphNodeByIndexedKindProperty predicate and exported FetchNodeByObjectIDIncludeOpenGraph which falls back to OpenGraph-indexed lookup when not found.
OpenAPI / API Surface
packages/go/openapi/doc/openapi.json, packages/go/openapi/src/paths/graph.graphs.shortest-path.yaml, packages/go/openapi/src/paths/graph-schema.edge-kinds.yaml
Added only_traversable boolean query parameter to GET /api/v2/graphs/shortest-path and clarified relationship_kinds description; removed OpenGraph tag from edge-kinds endpoint.
Schema & Codegen
packages/cue/bh/ad/ad.cue, packages/cue/bh/bh.cue, packages/go/schemagen/model/schema.go, packages/go/schemagen/generator/golang.go, packages/go/graphschema/ad/ad.go
Added PathfindingRelationshipsMatchFrontend CUE field and propagated it through generated Go model/generator and graphschema helper.
Minor config formatting
cmd/api/src/config/config.go, cmd/api/src/config/default.go
Whitespace/reformatting only for DisableAPIKeys default/config struct fields.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API as GetShortestPath (API)
    participant Flags as FlagsStore
    participant Filter as FilterBuilder
    participant GraphQ as GraphQuery
    participant Analysis as Analysis (Node Fetch)

    Client->>API: GET /api/v2/graphs/shortest-path?relationship_kinds=&only_traversable
    API->>Flags: GetFlagByKey(FeatureOpenGraphSearch)
    Flags-->>API: flag value
    API->>Filter: createRelationshipKindFilterCriteria(relationship_kinds, only_traversable, validKinds)
    alt OpenGraph enabled
        API->>GraphQ: GetAllShortestPathsWithOpenGraph(start,end,filter)
        GraphQ->>Analysis: FetchNodeByObjectIDIncludeOpenGraph(...)
        Analysis-->>GraphQ: Node (may be OpenGraph kind)
        GraphQ-->>API: PathSet
    else OpenGraph disabled
        API->>GraphQ: GetAllShortestPaths(start,end,filter)
        GraphQ->>Analysis: FetchNodeByObjectID(...)
        Analysis-->>GraphQ: Node
        GraphQ-->>API: PathSet
    end
    API-->>Client: HTTP Response (PathSet or error)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Possibly related PRs

Suggested reviewers

  • AD7ZJ
  • wes-mil
  • LawsonWillard
  • superlinkx

Poem

🐇 I hop through nodes both near and far,

only-traversable hops guide my star.
Flags whisper whether OpenGraph's on,
I fetch, I filter, the shortest path drawn.
Thump—I've found the route, carrot time!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main feature: adding OpenGraph node support to the shortest-path endpoint, with the Jira ticket reference.
Description check ✅ Passed The PR description covers all major required sections: clear problem statement, motivation (enables path searches for OpenGraph nodes), testing details (API/graph integration tests, local testing with screenshot), feature type (new feature), and completed checklist items.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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

Comment @coderabbitai help to get the list of available commands and usage tips.

@sirisjo sirisjo changed the title feat(OpenGraph): Add OpenGraph nodes to/shortest-path endpoint BED-6718 feat(OpenGraph): Add OpenGraph nodes to shortest-path endpoint BED-6718 Jan 10, 2026
@sirisjo sirisjo changed the title feat(OpenGraph): Add OpenGraph nodes to shortest-path endpoint BED-6718 feat(OpenGraph): Add OpenGraph nodes to shortest-path endpoint BED-6718 Jan 10, 2026
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

🤖 Fix all issues with AI agents
In @cmd/api/src/api/v2/pathfinding.go:
- Around line 146-148: Rename the local variable named `context` to `ctx` to
avoid shadowing the imported `context` package: change the declaration `context
= request.Context()` to `ctx := request.Context()` (or `var ctx context.Context
= request.Context()` if you prefer explicit typing) and update every use of
`context` in this function (the occurrences at the previously noted call sites)
to `ctx` (e.g., replace `context` with `ctx` in calls that pass the request
context into graph/path functions and error handling).

In @packages/go/openapi/doc/openapi.json:
- Around line 7246-7253: Update the "description" for the query parameter
"only_traversable" to explicitly state (1) when only_traversable=true and
relationship_kinds is omitted the API will restrict to all traversable
relationship kinds by default, (2) when only_traversable=false the API will
consider both traversable and non‑traversable kinds (i.e., no traversability
filtering) even if relationship_kinds is provided, and (3) when the parameter is
omitted document the default behavior (specify true or false as the default).
Keep the text concise and concrete so callers know exact behavior in the three
scenarios and include the default value.
🧹 Nitpick comments (6)
cmd/api/src/services/saml/mocks/saml.go (1)

1-1: Auto-generated file: consider regenerating instead of manual updates.

This file is auto-generated by MockGen (as indicated by the comment on line 17). Rather than manually updating the copyright header, consider regenerating this file using the mockgen command shown in the comments (line 22) to ensure consistency with the generation tooling.

For bulk license/header year updates across generated files, adding the copyright_file parameter to the mockgen invocation (already present: -copyright_file=../../../../../LICENSE.header) ensures headers stay in sync automatically on future regenerations.

packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/SelectedDetailsTabs/SelectedDetailsTabs.test.tsx (1)

176-176: Minor: copyright year inconsistency with other updated files.

The file still shows copyright 2025 (line 1) while other generated files in the PR have been updated to 2026. For consistency with the broader year-bump pattern, consider updating the copyright header if this file is part of the 2026 update cycle. The trailing newline addition is fine.

cmd/api/src/queries/graph_integration_test.go (1)

507-776: Consider using subtests for better test organization.

The test provides excellent coverage of OpenGraph pathfinding scenarios. However, it would benefit from wrapping each test case in t.Run(testCase.name, func(t *testing.T) {...}). This would:

  • Show which specific scenario failed in test output
  • Allow running individual test cases with -run
  • Follow Go testing best practices
♻️ Suggested refactor
 	defer teardownIntegrationTestSuite(t, &testSuite)
 
 	for _, testCase := range testTable {
-		actual, err := graphQuery.GetAllShortestPathsWithOpenGraph(testSuite.Context, testCase.startNodeID, testCase.endNodeID, testCase.filter)
-		require.Nil(t, err)
-		require.Equal(t, testCase.expected, actual)
+		t.Run(testCase.name, func(t *testing.T) {
+			actual, err := graphQuery.GetAllShortestPathsWithOpenGraph(testSuite.Context, testCase.startNodeID, testCase.endNodeID, testCase.filter)
+			require.Nil(t, err)
+			require.Equal(t, testCase.expected, actual)
+		})
 	}
 }
cmd/api/src/queries/graph.go (1)

284-315: Implementation is correct; consider reducing duplication.

The method correctly implements OpenGraph-aware pathfinding by using FetchNodeByObjectIDIncludeOpenGraph. The logic mirrors GetAllShortestPaths with only the node fetching function differing. While functional, you could optionally refactor both methods to share the common path-fetching logic through an internal helper function that accepts a node-fetching strategy.

♻️ Optional refactor to reduce duplication

Consider extracting shared logic into a helper:

func (s *GraphQuery) getAllShortestPathsInternal(ctx context.Context, startNodeID string, endNodeID string, filter graph.Criteria, nodeFetcher func(tx graph.Transaction, objectID string) (*graph.Node, error)) (graph.PathSet, error) {
	var paths graph.PathSet
	
	return paths, s.Graph.ReadTransaction(ctx, func(tx graph.Transaction) error {
		if startNode, err := nodeFetcher(tx, startNodeID); err != nil {
			return err
		} else if endNode, err := nodeFetcher(tx, endNodeID); err != nil {
			return err
		} else {
			criteria := []graph.Criteria{
				query.Equals(query.StartID(), startNode.ID),
				query.Equals(query.EndID(), endNode.ID),
			}
			
			if filter != nil {
				criteria = append(criteria, filter)
			}
			
			return tx.Relationships().Filter(query.And(criteria...)).FetchAllShortestPaths(func(cursor graph.Cursor[graph.Path]) error {
				for path := range cursor.Chan() {
					if len(path.Edges) > 0 {
						paths.AddPath(path)
					}
				}
				return cursor.Error()
			})
		}
	})
}

func (s *GraphQuery) GetAllShortestPaths(ctx context.Context, startNodeID string, endNodeID string, filter graph.Criteria) (graph.PathSet, error) {
	defer measure.ContextMeasure(ctx, slog.LevelInfo, "GetAllShortestPaths")()
	return s.getAllShortestPathsInternal(ctx, startNodeID, endNodeID, filter, analysis.FetchNodeByObjectID)
}

func (s *GraphQuery) GetAllShortestPathsWithOpenGraph(ctx context.Context, startNodeID string, endNodeID string, filter graph.Criteria) (graph.PathSet, error) {
	defer measure.ContextMeasure(ctx, slog.LevelInfo, "GetAllShortestPathsWithOpenGraph")()
	return s.getAllShortestPathsInternal(ctx, startNodeID, endNodeID, filter, analysis.FetchNodeByObjectIDIncludeOpenGraph)
}
cmd/api/src/api/v2/pathfinding_test.go (1)

821-823: Minor: Redundant parentheses around ad.Contains.

The extra parentheses don't affect functionality but are unnecessary.

Suggested fix
-					GetAllShortestPathsWithOpenGraph(gomock.Any(), "someID", "someOtherID", query.KindIn(query.Relationship(), traversableKindsWithOpenGraph.Exclude(graph.Kinds{}.Add((ad.Contains)))...)).
+					GetAllShortestPathsWithOpenGraph(gomock.Any(), "someID", "someOtherID", query.KindIn(query.Relationship(), traversableKindsWithOpenGraph.Exclude(graph.Kinds{}.Add(ad.Contains))...)).
cmd/api/src/api/v2/pathfinding.go (1)

114-124: Confusing logic: consider adding a clarifying comment or refactoring.

The function name relationshipKindExistsInGraph suggests it checks whether a kind exists, but it returns false unconditionally when onlyIncludeTraversableKinds is false. The actual purpose appears to be: "when requesting only traversable kinds, check if a non-traversable kind is still valid (exists in the full relationship set) so it can be silently filtered out instead of erroring."

Also note this function only checks built-in AD/Azure relationships and won't recognize OpenGraph edge kinds—this appears intentional since OpenGraph kinds would already be in acceptableKinds, but a comment would clarify the design.

Suggested documentation
-// Determines if the relationship kind is technically a valid built-in kind that exists in the graph
+// relationshipKindExistsInGraph checks if a kind is a valid built-in relationship kind.
+// This is used when onlyIncludeTraversableKinds=true to allow users to request
+// non-traversable kinds without error—those kinds are silently excluded from the filter
+// rather than returning a validation error. When onlyIncludeTraversableKinds=false,
+// all valid kinds should already be in acceptableKinds, so this fallback isn't needed.
+// Note: This only checks AD/Azure built-in kinds; OpenGraph kinds should already be
+// included in acceptableKinds when OpenGraph is enabled.
 func relationshipKindExistsInGraph(kind graph.Kind, onlyIncludeTraversableKinds bool) bool {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 658f84d and 8cdfef3.

📒 Files selected for processing (36)
  • LICENSE.header
  • cmd/api/src/api/constant.go
  • cmd/api/src/api/mocks/authenticator.go
  • cmd/api/src/api/v2/pathfinding.go
  • cmd/api/src/api/v2/pathfinding_internal_test.go
  • cmd/api/src/api/v2/pathfinding_test.go
  • cmd/api/src/daemons/datapipe/mocks/cleanup.go
  • cmd/api/src/database/mocks/auth.go
  • cmd/api/src/database/mocks/db.go
  • cmd/api/src/queries/fixtures/OpenGraphJSON/raw/base.json
  • cmd/api/src/queries/graph.go
  • cmd/api/src/queries/graph_integration_test.go
  • cmd/api/src/queries/mocks/graph.go
  • cmd/api/src/services/agi/mocks/mock.go
  • cmd/api/src/services/dataquality/mocks/mock.go
  • cmd/api/src/services/fs/mocks/fs.go
  • cmd/api/src/services/graphify/mocks/ingest.go
  • cmd/api/src/services/oidc/mocks/oidc.go
  • cmd/api/src/services/saml/mocks/saml.go
  • cmd/api/src/services/upload/mocks/mock.go
  • cmd/api/src/utils/validation/mocks/validator.go
  • cmd/api/src/vendormocks/dawgs/graph/mock.go
  • cmd/api/src/vendormocks/io/fs/mock.go
  • cmd/api/src/vendormocks/neo4j/neo4j-go-driver/v5/neo4j/mock.go
  • packages/csharp/graphschema/PropertyNames.cs
  • packages/go/analysis/analysis.go
  • packages/go/crypto/mocks/digest.go
  • packages/go/graphschema/ad/ad.go
  • packages/go/graphschema/azure/azure.go
  • packages/go/graphschema/common/common.go
  • packages/go/graphschema/graph.go
  • packages/go/openapi/doc/openapi.json
  • packages/go/openapi/src/paths/graph-schema.edge-kinds.yaml
  • packages/go/openapi/src/paths/graph.graphs.shortest-path.yaml
  • packages/javascript/bh-shared-ui/src/graphSchema.ts
  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/SelectedDetailsTabs/SelectedDetailsTabs.test.tsx
💤 Files with no reviewable changes (1)
  • packages/go/openapi/src/paths/graph-schema.edge-kinds.yaml
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-06-06T23:12:14.181Z
Learnt from: elikmiller
Repo: SpecterOps/BloodHound PR: 1563
File: packages/go/graphschema/azure/azure.go:24-24
Timestamp: 2025-06-06T23:12:14.181Z
Learning: In BloodHound, files in packages/go/graphschema/*/`*.go` are generated from CUE schemas. When `just prepare-for-codereview` is run, it triggers code generation that may automatically add import aliases or other formatting changes. These changes are legitimate outputs of the generation process, not manual edits that would be overwritten.

Applied to files:

  • packages/go/graphschema/ad/ad.go
  • packages/go/graphschema/azure/azure.go
  • packages/go/graphschema/graph.go
  • packages/go/graphschema/common/common.go
  • cmd/api/src/api/v2/pathfinding_test.go
📚 Learning: 2026-01-08T22:00:44.868Z
Learnt from: TheNando
Repo: SpecterOps/BloodHound PR: 2230
File: packages/javascript/bh-shared-ui/src/utils/object.ts:17-19
Timestamp: 2026-01-08T22:00:44.868Z
Learning: In the bh-shared-ui package, ensure that public utilities, types, and components are exported from the package’s API surface so they can be consumed by parent projects. This applies broadly to all files under packages/javascript/bh-shared-ui/src, not just this specific file. Avoid exporting internal, implementation-only modules that aren’t meant for external consumption; document and surface stable APIs for downstream users.

Applied to files:

  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/SelectedDetailsTabs/SelectedDetailsTabs.test.tsx
  • packages/javascript/bh-shared-ui/src/graphSchema.ts
📚 Learning: 2025-11-05T21:13:08.542Z
Learnt from: ktstrader
Repo: SpecterOps/BloodHound PR: 2020
File: cmd/api/src/queries/graph.go:198-203
Timestamp: 2025-11-05T21:13:08.542Z
Learning: In cmd/api/src/queries/graph.go, when ETAC filtering is enabled in GetAssetGroupComboNode, an empty etacAllowedList (when user.AllEnvironments is false and user.EnvironmentTargetedAccessControl is empty) is intentional and should not be guarded against. The empty list will cause query.In() to filter out all nodes, which is the correct security behavior—users with no environment assignments should see no results.

Applied to files:

  • cmd/api/src/queries/graph.go
📚 Learning: 2025-07-09T00:36:54.112Z
Learnt from: mistahj67
Repo: SpecterOps/BloodHound 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.

Applied to files:

  • cmd/api/src/queries/graph.go
📚 Learning: 2025-07-22T20:30:34.839Z
Learnt from: LawsonWillard
Repo: SpecterOps/BloodHound PR: 1700
File: cmd/api/src/api/v2/saved_queries_test.go:3182-3182
Timestamp: 2025-07-22T20:30:34.839Z
Learning: In Go table-driven tests in cmd/api/src/api/v2/saved_queries_test.go, subtest parallelization with t.Parallel() is acceptable when tests are self-contained, each creating their own mock controller (gomock.NewController(t)) and having isolated mock expectations without shared state between subtests.

Applied to files:

  • cmd/api/src/queries/graph_integration_test.go
  • cmd/api/src/api/v2/pathfinding_test.go
📚 Learning: 2025-05-29T18:42:23.137Z
Learnt from: superlinkx
Repo: SpecterOps/BloodHound PR: 1509
File: packages/go/stbernard/command/license/internal/utils.go:77-86
Timestamp: 2025-05-29T18:42:23.137Z
Learning: Copyright years in license headers should typically preserve the original year when the work was created, not be updated to the current year when the license is added later. The engineering team (#bhe-engineering, bhe-ec) should provide guidance on the copyright year policy for the BloodHound project.

Applied to files:

  • LICENSE.header
📚 Learning: 2025-08-28T16:43:43.961Z
Learnt from: mvlipka
Repo: SpecterOps/BloodHound PR: 1784
File: packages/go/openapi/doc/openapi.json:18008-18029
Timestamp: 2025-08-28T16:43:43.961Z
Learning: In SpecterOps/BloodHound, packages/go/openapi/doc/openapi.json is generated from YAML under packages/go/openapi/src/schemas; edits must be made to the YAML and then the spec regenerated.

Applied to files:

  • packages/go/openapi/doc/openapi.json
🧬 Code graph analysis (4)
packages/go/analysis/analysis.go (3)
packages/go/graphschema/ad/ad.go (2)
  • Entity (28-28)
  • Nodes (1146-1148)
packages/go/graphschema/azure/azure.go (1)
  • Entity (28-28)
packages/go/graphschema/common/common.go (2)
  • Nodes (33-35)
  • ObjectID (52-52)
cmd/api/src/queries/graph.go (2)
packages/go/analysis/analysis.go (1)
  • FetchNodeByObjectIDIncludeOpenGraph (110-119)
packages/go/graphschema/graph.go (1)
  • Path (35-39)
cmd/api/src/queries/graph_integration_test.go (2)
packages/go/graphschema/graph.go (1)
  • Path (35-39)
packages/go/lab/generic/graph.go (1)
  • Node (36-40)
cmd/api/src/api/v2/pathfinding_test.go (6)
cmd/api/src/database/mocks/db.go (1)
  • NewMockDatabase (55-59)
cmd/api/src/queries/graph.go (1)
  • GraphQuery (161-169)
cmd/api/src/model/graphschema.go (1)
  • GraphSchemaEdgeKindsWithNamedSchema (146-146)
cmd/api/src/model/appcfg/flag.go (2)
  • FeatureOpenGraphSearch (44-44)
  • FeatureFlag (50-70)
packages/go/graphschema/ad/ad.go (2)
  • GenericAll (45-45)
  • GenericWrite (46-46)
packages/go/graphschema/azure/azure.go (1)
  • ServicePrincipalEndpointReadWriteAll (84-84)
⏰ 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: run-analysis
  • GitHub Check: build-ui
🔇 Additional comments (36)
packages/csharp/graphschema/PropertyNames.cs (1)

2-2: Copyright year update looks good.

The copyright year has been correctly updated from 2025 to 2026. No functional changes.

packages/javascript/bh-shared-ui/src/graphSchema.ts (1)

1-1: LGTM!

Copyright year update only. The file correctly exports all public schema enums, display functions, and pathfinding helpers for downstream consumers.

cmd/api/src/services/dataquality/mocks/mock.go (1)

1-1: LGTM!

Auto-generated mock file with copyright year update. No functional changes.

cmd/api/src/services/upload/mocks/mock.go (1)

1-1: LGTM!

Auto-generated mock file with copyright year update. No functional changes.

packages/go/graphschema/graph.go (1)

1-1: LGTM!

Auto-generated file from CUE schemas with copyright year update. No functional changes. Based on learnings, these changes are legitimate outputs of the generation process.

cmd/api/src/api/mocks/authenticator.go (1)

1-1: LGTM!

Auto-generated mock file with copyright year update. No functional changes.

cmd/api/src/vendormocks/neo4j/neo4j-go-driver/v5/neo4j/mock.go (1)

1-1: LGTM!

Auto-generated mock file with copyright year update. No functional changes.

LICENSE.header (1)

1-1: Copyright year update noted.

The copyright year has been updated to 2026. Based on learnings, there may be a project-level policy consideration regarding whether copyright years should preserve the original year or be updated annually. If this hasn't been formalized, the engineering team may want to document the preferred approach.

cmd/api/src/services/graphify/mocks/ingest.go (1)

1-1: LGTM!

Auto-generated mock file with copyright year update. No functional changes.

packages/go/graphschema/azure/azure.go (1)

1-1: Routine copyright update in generated file.

cmd/api/src/vendormocks/io/fs/mock.go (1)

1-1: Routine copyright update in generated mock file.

packages/go/crypto/mocks/digest.go (1)

1-1: Routine copyright update in generated mock file.

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

1-1: Copyright year updated as part of repository maintenance.

This auto-generated mock file has been updated with the 2026 copyright year. No functional changes.

cmd/api/src/services/oidc/mocks/oidc.go (1)

1-1: Copyright year updated as part of repository maintenance.

This auto-generated mock file has been updated with the 2026 copyright year. No functional changes.

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

1-1: Copyright year updated as part of repository maintenance.

This auto-generated mock file has been updated with the 2026 copyright year. No functional changes to the mock interface or methods.

cmd/api/src/daemons/datapipe/mocks/cleanup.go (1)

1-1: Skip: auto-generated mock file.

This is machine-generated code marked "DO NOT EDIT" (line 17). The copyright year bump is routine and expected for auto-generated mocks.

cmd/api/src/utils/validation/mocks/validator.go (1)

1-1: Skip: auto-generated mock file.

This is machine-generated code marked "DO NOT EDIT" (line 17). The copyright year bump is routine and expected for auto-generated mocks.

cmd/api/src/vendormocks/dawgs/graph/mock.go (1)

1-1: LGTM: Copyright year updated.

The copyright year has been appropriately updated to 2026 for this generated mock file.

packages/go/graphschema/ad/ad.go (1)

1-1: LGTM: Copyright year updated in generated file.

The copyright year has been appropriately updated to 2026. This file is generated from CUE schemas, and the change is part of the standard code generation process.

Based on learnings, files in packages/go/graphschema/*/ are automatically generated and such updates are expected.

cmd/api/src/api/constant.go (1)

42-51: LGTM: New query parameter constant added.

The new QueryParameterIncludeOnlyTraversableKinds constant follows the established naming convention and aligns with the OpenGraph pathfinding feature introduced in this PR. The alignment improvements to existing constants also enhance readability.

packages/go/analysis/analysis.go (2)

88-93: LGTM: OpenGraph node filtering criteria added.

The openGraphNodeByIndexedKindProperty function correctly creates a query criteria that matches nodes by property while excluding AD and Azure base entities (ad.Entity and azure.Entity), enabling OpenGraph-specific node lookups.


110-119: LGTM: OpenGraph-inclusive node lookup implemented correctly.

The FetchNodeByObjectIDIncludeOpenGraph function properly implements a fallback pattern:

  1. First attempts standard AD/Azure entity lookup via FetchNodeByObjectID
  2. Falls back to OpenGraph-aware query only when the node is not found
  3. Correctly propagates non-NotFound errors immediately

This design enables OpenGraph node retrieval while maintaining backward compatibility with existing node lookup logic.

packages/go/openapi/src/paths/graph.graphs.shortest-path.yaml (2)

41-41: LGTM: Clear error behavior documented.

The updated description for relationship_kinds appropriately documents that invalid kinds will result in an error, setting clear expectations for API consumers.


45-50: LGTM: New parameter documented with clear interaction behavior.

The only_traversable parameter is well-documented with:

  • Clear description of its purpose
  • Explanation of interaction with relationship_kinds parameter
  • Appropriate schema definition (boolean, optional)

The documentation makes it clear that when enabled, non-traversable kinds specified in relationship_kinds will be filtered out, which is the expected behavior.

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

30-69: LGTM! Comprehensive test coverage for the new parameter.

The test additions properly validate the onlyTraversable parameter behavior, including error cases and filtering of non-traversable relationship kinds.

cmd/api/src/queries/fixtures/OpenGraphJSON/raw/base.json (1)

1-168: LGTM! Clear fixture structure for OpenGraph testing.

The restructuring with explicit edge definitions and numeric node IDs provides a clean foundation for the OpenGraph pathfinding tests.

cmd/api/src/queries/graph_integration_test.go (1)

139-139: LGTM! ObjectID expectations updated to match fixture.

The test expectations correctly reflect the fixture changes from "TEST-1" to numeric string IDs.

Also applies to: 226-226

cmd/api/src/queries/graph.go (1)

139-139: LGTM! Clean interface extension.

The new method signature appropriately extends the Graph interface to support OpenGraph pathfinding.

cmd/api/src/queries/mocks/graph.go (1)

1-1: LGTM! Mock properly implements the new interface method.

The auto-generated mock correctly supports the new GetAllShortestPathsWithOpenGraph method and follows the established pattern. Copyright year appropriately updated to 2026.

Also applies to: 199-212

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

100-118: LGTM! Comprehensive test setup for OpenGraph pathfinding scenarios.

The test variables are well-organized, covering both standard and OpenGraph-enabled pathfinding with proper kind filters. The setup correctly initializes built-in kinds concatenated with OpenGraph kinds for testing various filter scenarios.


415-478: LGTM! Good coverage for the only_traversable parameter behavior.

These test cases properly verify that:

  1. Invalid boolean values default to false without erroring
  2. The traversable filter restricts to pathfinding relationships
  3. Combining only_traversable with relationship_kinds correctly intersects the filters

The test at line 470 correctly expects only ad.Contains since AZScopedTo is not a traversable relationship.

cmd/api/src/api/v2/pathfinding.go (4)

80-112: LGTM! Parameter parsing logic is well-structured.

The function correctly:

  1. Validates format with regex
  2. Parses the operator and kinds
  3. Validates each kind against acceptable kinds or checks graph existence
  4. Returns appropriate defaults when no parameter is provided

126-138: LGTM! Clean filter criteria construction.

The function properly handles both inclusion (in) and exclusion (nin) operators, and correctly defaults to including all valid kinds when onlyIncludeTraversableKinds is true with no explicit parameter.


152-155: Intentional: invalid only_traversable defaults to false.

The error is logged but not returned, allowing requests with invalid boolean values to proceed with the default. This is verified by the test case "InvalidOnlyTraversableParamDefaultsToFalse".

Consider whether a 400 Bad Request would be more appropriate for invalid parameter values, or if the silent fallback is the desired UX.


194-217: LGTM! OpenGraph path retrieval logic is sound.

The function correctly:

  1. Builds filters for traversability when requested
  2. Fetches OpenGraph edge kinds from the database
  3. Merges OpenGraph kinds with built-in kinds
  4. Creates appropriate filter criteria and queries the graph

Minor nit: inconsistent blank line at line 211 before } else {.

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

7237-7254: Reminder: This file is generated from YAML sources.

Based on learnings, packages/go/openapi/doc/openapi.json is generated from YAML under packages/go/openapi/src/schemas. Ensure that the source YAML files (graph.graphs.shortest-path.yaml) have been updated and the spec regenerated.

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

🤖 Fix all issues with AI agents
In @cmd/api/src/api/v2/pathfinding.go:
- Around line 184-192: In getAllShortestPaths change the parameter named context
to ctx to avoid shadowing the imported context package: update the function
signature in Resources.getAllShortestPaths to use ctx, and replace all uses of
the parameter (e.g., in the call to s.GraphQuery.GetAllShortestPaths) to pass
ctx instead of context; ensure any callers or references to this function
(within the same diff) are adjusted if they use the old name inside this
function body, keeping the rest of logic unchanged.
- Around line 194-217: The function getAllShortestPathsWithOpenGraph uses a
parameter named context which shadows the imported context package; rename the
parameter to ctx in the function signature and update all uses inside the
function (e.g., calls to s.DB.GetGraphSchemaEdgeKindsWithSchemaName(ctx, ...),
s.GraphQuery.GetAllShortestPathsWithOpenGraph(ctx, ...)) so the package import
remains accessible and naming matches other functions like getAllShortestPaths;
no other logic changes needed.
🧹 Nitpick comments (2)
cmd/api/src/api/v2/pathfinding.go (2)

114-124: Confusing function semantics and logic.

The function name relationshipKindExistsInGraph suggests it checks whether a kind exists in the graph, but the actual logic returns false when onlyIncludeTraversableKinds is false (line 117-118). This is counterintuitive—when not filtering to traversable-only, a valid built-in kind should still "exist."

The function effectively answers: "Is this a valid built-in kind AND are we in traversable-only mode?" Consider renaming for clarity, e.g., isValidBuiltInKindForTraversableMode.

Additionally, the nested if-else can be simplified:

♻️ Suggested simplification
 func relationshipKindExistsInGraph(kind graph.Kind, onlyIncludeTraversableKinds bool) bool {
 	validBuiltInKinds := graph.Kinds(ad.Relationships()).Concatenate(azure.Relationships())
-	if !onlyIncludeTraversableKinds {
-		return false
-	} else if !validBuiltInKinds.ContainsOneOf(kind) {
-		return false
-	} else {
-		return true
-	}
+	return onlyIncludeTraversableKinds && validBuiltInKinds.ContainsOneOf(kind)
 }

152-155: Consider improving error handling for malformed boolean parameter.

When ParseOptionalBool fails, the error is logged but the request continues with the default value (false). While this is reasonable for optional parameters, returning a 400 Bad Request for a malformed boolean value would be more explicit and help API consumers identify their mistake.

♻️ Optional: Return 400 for malformed boolean
 	onlyIncludeTraversableKinds, err := api.ParseOptionalBool(request.URL.Query().Get(api.QueryParameterIncludeOnlyTraversableKinds), false)
 	if err != nil {
-		slog.ErrorContext(ctx, err.Error())
+		api.WriteErrorResponse(ctx, api.BuildErrorResponse(http.StatusBadRequest, fmt.Sprintf("Invalid value for '%s': expected boolean", api.QueryParameterIncludeOnlyTraversableKinds), request), response)
+		return
 	}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8cdfef3 and e299e67.

📒 Files selected for processing (3)
  • cmd/api/src/api/v2/pathfinding.go
  • packages/go/openapi/doc/openapi.json
  • packages/go/openapi/src/paths/graph.graphs.shortest-path.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/go/openapi/doc/openapi.json
🧰 Additional context used
🧬 Code graph analysis (1)
cmd/api/src/api/v2/pathfinding.go (7)
cmd/api/src/api/error.go (4)
  • ErrorWrapper (111-116)
  • BuildErrorResponse (134-145)
  • HandleDatabaseError (149-158)
  • FormatDatabaseError (162-169)
cmd/api/src/ctx/ctx.go (1)
  • Get (75-85)
cmd/api/src/api/constant.go (1)
  • QueryParameterIncludeOnlyTraversableKinds (51-51)
cmd/api/src/api/marshalling.go (1)
  • WriteErrorResponse (77-85)
cmd/api/src/model/appcfg/flag.go (1)
  • FeatureOpenGraphSearch (44-44)
cmd/api/src/api/v2/model.go (1)
  • Resources (105-118)
cmd/api/src/model/filter.go (3)
  • Filters (485-485)
  • Filter (479-483)
  • Sort (507-507)
⏰ 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: run-analysis
  • GitHub Check: build-ui
🔇 Additional comments (4)
packages/go/openapi/src/paths/graph.graphs.shortest-path.yaml (1)

40-50: LGTM - Well-documented new parameter.

The only_traversable parameter is well-documented with clear explanations of its behavior in different scenarios (true with/without relationship_kinds, false/omitted). The updated relationship_kinds description appropriately clarifies the error behavior for invalid kinds.

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

98-105: Potential silent filtering of valid relationship kinds.

When a kind is not in acceptableKinds but passes relationshipKindExistsInGraph (returns true when onlyTraversable is true and kind is a valid built-in), it's silently dropped from parameterKinds without being added or causing an error. This means user-specified built-in non-traversable kinds would be silently ignored when onlyTraversable=true.

Is this the intended behavior? If so, consider documenting this in the OpenAPI description. If not, the kind should likely be added to parameterKinds when it exists in the graph.


126-138: LGTM - Filter criteria logic is correct.

The function properly handles both in and nin operations, with the nin case correctly excluding specified kinds from the valid set. The early return for traversable-only mode without specified kinds is a clean optimization.


162-181: LGTM - Feature flag integration and routing logic.

The feature flag retrieval with proper database error handling, the conditional assignment of validBuiltInKinds based on onlyIncludeTraversableKinds, and the clean branching between OpenGraph and standard pathfinding are well-structured.

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 (2)
cmd/api/src/queries/graph_integration_test.go (1)

507-518: Minor: Test function name uses singular "Path" vs method name uses "Paths".

The test function is named TestGetAllShortestPathWithOpenGraph but calls GetAllShortestPathsWithOpenGraph (plural). Consider renaming for consistency:

-func TestGetAllShortestPathWithOpenGraph(t *testing.T) {
+func TestGetAllShortestPathsWithOpenGraph(t *testing.T) {
cmd/api/src/api/v2/pathfinding.go (1)

146-149: Consider consistent error handling for malformed boolean parameters.

The ParseOptionalBool error is currently logged but execution continues with the default value of false. While the test explicitly validates this behavior (InvalidOnlyTraversableParamDefaultsToFalse in pathfinding_test.go confirms malformed input like only_traversable=notABool silently defaults to false), this approach differs from other endpoints. For example, ad_entity.go returns HTTP 400 when the same function encounters an error (lines 67-68). Consider standardizing this behavior across the API—either validate and reject malformed booleans consistently, or document the intentional lenient defaulting pattern.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e299e67 and caadf56.

📒 Files selected for processing (4)
  • cmd/api/src/api/v2/pathfinding.go
  • cmd/api/src/api/v2/pathfinding_test.go
  • cmd/api/src/queries/graph.go
  • cmd/api/src/queries/graph_integration_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/api/src/queries/graph.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-06T23:12:14.181Z
Learnt from: elikmiller
Repo: SpecterOps/BloodHound PR: 1563
File: packages/go/graphschema/azure/azure.go:24-24
Timestamp: 2025-06-06T23:12:14.181Z
Learning: In BloodHound, files in packages/go/graphschema/*/`*.go` are generated from CUE schemas. When `just prepare-for-codereview` is run, it triggers code generation that may automatically add import aliases or other formatting changes. These changes are legitimate outputs of the generation process, not manual edits that would be overwritten.

Applied to files:

  • cmd/api/src/api/v2/pathfinding_test.go
📚 Learning: 2025-07-22T20:30:34.839Z
Learnt from: LawsonWillard
Repo: SpecterOps/BloodHound PR: 1700
File: cmd/api/src/api/v2/saved_queries_test.go:3182-3182
Timestamp: 2025-07-22T20:30:34.839Z
Learning: In Go table-driven tests in cmd/api/src/api/v2/saved_queries_test.go, subtest parallelization with t.Parallel() is acceptable when tests are self-contained, each creating their own mock controller (gomock.NewController(t)) and having isolated mock expectations without shared state between subtests.

Applied to files:

  • cmd/api/src/api/v2/pathfinding_test.go
  • cmd/api/src/queries/graph_integration_test.go
🧬 Code graph analysis (2)
cmd/api/src/queries/graph_integration_test.go (3)
cmd/api/src/queries/graph.go (1)
  • NewGraphQuery (171-181)
packages/go/graphschema/graph.go (1)
  • Path (35-39)
packages/go/lab/generic/graph.go (1)
  • Node (36-40)
cmd/api/src/api/v2/pathfinding.go (2)
cmd/api/src/api/constant.go (1)
  • QueryParameterIncludeOnlyTraversableKinds (51-51)
cmd/api/src/model/appcfg/flag.go (1)
  • FeatureOpenGraphSearch (44-44)
⏰ 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 (12)
cmd/api/src/queries/graph_integration_test.go (3)

139-146: LGTM: Test value updated to match fixture data.

The change from "TEST-1" to "2" aligns with the updated fixture data in the OpenGraph JSON files where object IDs are now numeric strings.


224-232: LGTM: Consistent with fixture updates.


771-777: LGTM: Comprehensive integration tests for OpenGraph pathfinding.

The test cases provide good coverage of:

  • OpenGraph-to-OpenGraph paths
  • AD-to-AD paths
  • Cross-graph (OpenGraph-to-AD) paths
  • Kind filtering with exclusion and inclusion

The table-driven approach with explicit expected results ensures precise validation of the pathfinding behavior.

cmd/api/src/api/v2/pathfinding_test.go (4)

100-118: LGTM: Well-structured test setup with comprehensive filter definitions.

The test setup properly defines:

  • Mock database for feature flag retrieval
  • OpenGraph edge kinds for testing
  • Multiple filter combinations (all kinds, traversable kinds, with/without OpenGraph)

This allows thorough testing of the various pathfinding scenarios.


144-161: LGTM: Proper error handling test for feature flag retrieval failure.

The test correctly verifies that a database error during feature flag lookup results in a 500 status code with a generic error message, avoiding information leakage.


415-478: LGTM: Thorough testing of only_traversable parameter behavior.

The tests properly verify:

  1. Invalid boolean values default to false (line 416-435)
  2. only_traversable=true restricts to traversable relationships only (line 437-456)
  3. Combining only_traversable=true with relationship_kinds filters correctly intersects the constraints (line 458-478)

762-830: LGTM: Comprehensive OpenGraph traversable relationship tests.

The tests properly verify:

  1. only_traversable=true passes the correct filter to GetGraphSchemaEdgeKindsWithSchemaName (line 773, 796, 820)
  2. Both IN and NIN operators work correctly with OpenGraph and traversable filtering
  3. The filter {"is_traversable": [{Operator: model.Equals, Value: "true"}]} is correctly applied
cmd/api/src/api/v2/pathfinding.go (5)

80-112: LGTM: Enhanced relationship kinds parsing with traversable filtering.

The updated parseRelationshipKindsParam function properly:

  1. Validates the format with regex
  2. Filters kinds based on acceptableKinds
  3. Silently ignores non-traversable but valid kinds when onlyTraversable=true (line 102-104)
  4. Returns an error for completely invalid kinds

The silent filtering behavior for non-traversable kinds is reasonable since it allows users to specify kinds without needing to know which are traversable.


114-118: LGTM: Helper correctly identifies non-traversable valid kinds.

The logic correctly determines if a kind exists in the built-in graph but should be silently ignored because it's not traversable. This enables the silent filtering behavior documented in the PR.


120-132: LGTM: Clean filter criteria creation logic.

The function properly handles:

  1. onlyTraversable=true without relationship_kinds → returns all traversable kinds
  2. in:Kind1,Kind2 → includes only specified kinds
  3. nin:Kind1,Kind2 → excludes specified kinds from valid set

178-186: LGTM: Clean implementation of standard shortest paths retrieval.

Proper error handling with distinct status codes:

  • 400 for invalid filter criteria
  • 500 for graph query errors

188-211: LGTM: OpenGraph-aware pathfinding implementation.

The method correctly:

  1. Applies is_traversable filter when retrieving OpenGraph edge kinds (line 190-192)
  2. Extends validKinds with OpenGraph edge kinds (line 200)
  3. Uses the combined kinds for filter creation and path retrieval

Minor formatting: there are extra blank lines at 205 and 210-211, but this doesn't affect functionality.

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)
cmd/api/src/queries/graph_integration_test.go (1)

507-778: Consider making edge ID assertions more resilient.

The test cases hardcode expected edge IDs (e.g., ID: 1, ID: 4, ID: 7) which are typically auto-assigned by the database. If insertion order changes or the database implementation varies, these tests could become flaky.

Consider one of these approaches:

  1. Assert on path structure (node sequence, edge kinds, start/end IDs) without checking the edge ID field.
  2. Use a custom comparison helper that ignores auto-generated IDs.
  3. Zero out the edge IDs in both expected and actual results before comparison.

Example helper approach:

func assertPathSetEquivalent(t *testing.T, expected, actual graph.PathSet) {
    require.Equal(t, len(expected), len(actual))
    for i := range expected {
        require.Equal(t, len(expected[i].Nodes), len(actual[i].Nodes))
        require.Equal(t, len(expected[i].Edges), len(actual[i].Edges))
        for j, edge := range actual[i].Edges {
            require.Equal(t, expected[i].Edges[j].StartID, edge.StartID)
            require.Equal(t, expected[i].Edges[j].EndID, edge.EndID)
            require.Equal(t, expected[i].Edges[j].Kind, edge.Kind)
        }
        // Similar for nodes...
    }
}

If edge IDs are guaranteed stable due to controlled fixture loading, please add a comment explaining why the hardcoded IDs are reliable.

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

7252-7260: Consider clarifying the edge case behavior.

The description is comprehensive. However, it might be worth clarifying what happens when only_traversable=true and all specified relationship_kinds are non-traversable. Would this result in an empty result set, an error, or default to searching all traversable kinds?

If this edge case is already handled gracefully (e.g., returns empty or falls back to all traversable), explicitly documenting this would help API consumers avoid unexpected behavior.

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

146-149: Silent default to false on parse error is intentional but consider explicit behavior.

The error is logged but execution continues with onlyIncludeTraversableKinds = false. While this is validated by the test case "InvalidOnlyTraversableParamDefaultsToFalse", consider whether silently defaulting is the desired UX. An alternative would be to return a 400 error for malformed boolean values.

Alternative: Return 400 for invalid boolean parameter
 onlyIncludeTraversableKinds, err := api.ParseOptionalBool(request.URL.Query().Get(api.QueryParameterIncludeOnlyTraversableKinds), false)
 if err != nil {
-    slog.ErrorContext(ctx, err.Error())
+    api.WriteErrorResponse(ctx, api.BuildErrorResponse(http.StatusBadRequest, fmt.Sprintf("Invalid value for %s: must be 'true' or 'false'", api.QueryParameterIncludeOnlyTraversableKinds), request), response)
+    return
 }

188-211: OpenGraph path retrieval correctly extends valid kinds.

The implementation:

  1. Builds appropriate filters for is_traversable when needed
  2. Fetches OpenGraph edge kinds from database
  3. Concatenates with built-in kinds before filter creation
  4. Uses the OpenGraph-aware graph query method

Minor formatting: there are extra blank lines (205-206, 210-211) that could be cleaned up, but this doesn't affect functionality.

Optional: Clean up extra blank lines
 	} else if paths, err := s.GraphQuery.GetAllShortestPathsWithOpenGraph(ctx, startNode, endNode, kindFilter); err != nil {
 		return nil, api.BuildErrorResponse(http.StatusInternalServerError, err.Error(), request)
-
 	} else {
 		return paths, nil
 	}
-
 }
-
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between caadf56 and 65d5963.

📒 Files selected for processing (13)
  • cmd/api/src/api/constant.go
  • cmd/api/src/api/v2/pathfinding.go
  • cmd/api/src/api/v2/pathfinding_internal_test.go
  • cmd/api/src/api/v2/pathfinding_test.go
  • cmd/api/src/queries/fixtures/OpenGraphJSON/raw/base.json
  • cmd/api/src/queries/graph.go
  • cmd/api/src/queries/graph_integration_test.go
  • cmd/api/src/queries/mocks/graph.go
  • cmd/ui/public/mockServiceWorker.js
  • packages/go/analysis/analysis.go
  • packages/go/openapi/doc/openapi.json
  • packages/go/openapi/src/paths/graph-schema.edge-kinds.yaml
  • packages/go/openapi/src/paths/graph.graphs.shortest-path.yaml
💤 Files with no reviewable changes (1)
  • packages/go/openapi/src/paths/graph-schema.edge-kinds.yaml
✅ Files skipped from review due to trivial changes (1)
  • cmd/ui/public/mockServiceWorker.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/go/openapi/src/paths/graph.graphs.shortest-path.yaml
  • cmd/api/src/queries/mocks/graph.go
  • packages/go/analysis/analysis.go
  • cmd/api/src/api/v2/pathfinding_internal_test.go
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-07-22T20:30:34.839Z
Learnt from: LawsonWillard
Repo: SpecterOps/BloodHound PR: 1700
File: cmd/api/src/api/v2/saved_queries_test.go:3182-3182
Timestamp: 2025-07-22T20:30:34.839Z
Learning: In Go table-driven tests in cmd/api/src/api/v2/saved_queries_test.go, subtest parallelization with t.Parallel() is acceptable when tests are self-contained, each creating their own mock controller (gomock.NewController(t)) and having isolated mock expectations without shared state between subtests.

Applied to files:

  • cmd/api/src/queries/graph_integration_test.go
  • cmd/api/src/api/v2/pathfinding_test.go
📚 Learning: 2025-06-06T23:12:14.181Z
Learnt from: elikmiller
Repo: SpecterOps/BloodHound PR: 1563
File: packages/go/graphschema/azure/azure.go:24-24
Timestamp: 2025-06-06T23:12:14.181Z
Learning: In BloodHound, files in packages/go/graphschema/*/`*.go` are generated from CUE schemas. When `just prepare-for-codereview` is run, it triggers code generation that may automatically add import aliases or other formatting changes. These changes are legitimate outputs of the generation process, not manual edits that would be overwritten.

Applied to files:

  • cmd/api/src/api/v2/pathfinding_test.go
📚 Learning: 2025-11-05T21:13:08.542Z
Learnt from: ktstrader
Repo: SpecterOps/BloodHound PR: 2020
File: cmd/api/src/queries/graph.go:198-203
Timestamp: 2025-11-05T21:13:08.542Z
Learning: In cmd/api/src/queries/graph.go, when ETAC filtering is enabled in GetAssetGroupComboNode, an empty etacAllowedList (when user.AllEnvironments is false and user.EnvironmentTargetedAccessControl is empty) is intentional and should not be guarded against. The empty list will cause query.In() to filter out all nodes, which is the correct security behavior—users with no environment assignments should see no results.

Applied to files:

  • cmd/api/src/queries/graph.go
📚 Learning: 2025-07-09T00:36:54.112Z
Learnt from: mistahj67
Repo: SpecterOps/BloodHound 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.

Applied to files:

  • cmd/api/src/queries/graph.go
🧬 Code graph analysis (3)
cmd/api/src/queries/graph_integration_test.go (3)
cmd/api/src/queries/graph.go (1)
  • NewGraphQuery (171-181)
packages/go/graphschema/graph.go (1)
  • Path (35-39)
packages/go/lab/generic/graph.go (1)
  • Node (36-40)
cmd/api/src/api/v2/pathfinding_test.go (4)
cmd/api/src/database/mocks/db.go (1)
  • NewMockDatabase (55-59)
cmd/api/src/model/graphschema.go (1)
  • GraphSchemaEdgeKindsWithNamedSchema (158-158)
cmd/api/src/model/appcfg/flag.go (2)
  • FeatureOpenGraphSearch (44-44)
  • FeatureFlag (50-70)
packages/go/graphschema/azure/azure.go (1)
  • ServicePrincipalEndpointReadWriteAll (84-84)
cmd/api/src/queries/graph.go (1)
packages/go/analysis/analysis.go (2)
  • FetchNodeByObjectID (98-108)
  • FetchNodeByObjectIDIncludeOpenGraph (110-119)
⏰ 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). (5)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-analysis
  • GitHub Check: build-ui
  • GitHub Check: run-tests
  • GitHub Check: run-go-unit-tests
🔇 Additional comments (13)
cmd/api/src/api/constant.go (1)

42-51: LGTM!

The new QueryParameterIncludeOnlyTraversableKinds constant follows existing naming conventions and the value aligns with the API documentation. The alignment padding improves readability.

cmd/api/src/queries/fixtures/OpenGraphJSON/raw/base.json (1)

1-167: LGTM!

The fixture provides well-structured test data for OpenGraph pathfinding scenarios. The graph topology with Knows, IsParent, and Contains edges enables testing shortest paths between OpenGraph nodes, AD nodes, and cross-graph paths—aligning with the integration test cases.

cmd/api/src/queries/graph_integration_test.go (2)

139-139: LGTM!

The change from the previous ObjectID to "2" aligns with the updated fixture where node IDs are now numeric strings.


226-226: LGTM!

Consistent with the fixture update.

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

7245-7250: LGTM!

The updated description clarifies error behavior when invalid relationship kinds are provided, which improves API documentation quality.

cmd/api/src/api/v2/pathfinding_test.go (4)

100-118: LGTM! Well-structured test setup with comprehensive test data.

The test data setup cleanly defines all the necessary kinds and filters for testing both standard and OpenGraph pathfinding scenarios. The separation of validBuiltInKinds vs validBuiltInTraversableKinds and their OpenGraph counterparts enables precise mock assertions.


415-456: Good coverage of only_traversable parameter behavior.

These tests verify:

  1. Invalid boolean values default to false (line 416-435)
  2. When true, only traversable relationships are used in the filter (line 437-456)
  3. The mock expectations correctly assert the filter contains only traversable kinds

457-478: Correctly tests filtering of non-traversable kinds from explicit in list.

This test verifies that when only_traversable=true and the user requests in:Contains,AZScopedTo, only ad.Contains is included (since AZScopedTo is valid but not traversable). This aligns with the implementation's silent-ignore behavior for valid-but-non-traversable kinds.


762-830: Comprehensive OpenGraph + only_traversable tests.

These tests properly verify:

  1. The is_traversable filter is passed to GetGraphSchemaEdgeKindsWithSchemaName (line 773, 796, 820)
  2. Both in and nin operations work correctly with OpenGraph and traversable filtering
  3. OpenGraph edge kinds are included in the filter criteria
cmd/api/src/queries/graph.go (2)

139-139: Good interface extension for OpenGraph support.

Adding GetAllShortestPathsWithOpenGraph to the interface maintains clean separation while enabling OpenGraph-aware pathfinding. The method signature mirrors GetAllShortestPaths for consistency.


251-289: Clean refactoring using strategy pattern for node fetching.

The introduction of getAllShortestPathsInternal with a nodeFetcher function parameter is a solid approach:

  • Eliminates code duplication between standard and OpenGraph path retrieval
  • The nodeFetcher signature func(tx graph.Transaction, objectID string) (*graph.Node, error) matches both analysis.FetchNodeByObjectID and analysis.FetchNodeByObjectIDIncludeOpenGraph
  • Error handling properly propagates node fetch failures
  • Logging via measure.ContextMeasure is appropriately placed in the public methods
cmd/api/src/api/v2/pathfinding.go (2)

80-112: Well-designed parameterized kind validation.

The refactored parseRelationshipKindsParam cleanly separates validation logic:

  1. Validates format via regex
  2. Filters kinds against acceptableKinds (which varies based on onlyTraversable and OpenGraph state)
  3. Silently ignores valid-but-non-traversable kinds when appropriate (line 102-104)

The logic at lines 100-104 is correct:

  • If kind is in acceptableKinds → include it
  • Else if kind is valid but non-traversable AND onlyTraversable=true → silently skip
  • Else → return error (truly invalid kind)

114-118: Correct helper for traversability check.

This helper correctly determines if a kind exists in the built-in graph but is not in the traversable subset. The short-circuit with onlyIncludeTraversableKinds ensures the function only performs the check when relevant.

Copy link
Member

@AD7ZJ AD7ZJ left a comment

Choose a reason for hiding this comment

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

Couple minor q's but overall looks good! This is a big ticket, nice work.

if onlyIncludeTraversableKinds {
edgeKindFilters["is_traversable"] = append(edgeKindFilters["is_traversable"], model.Filter{Operator: model.Equals, Value: "true"})
}
if openGraphEdges, _, err := s.DB.GetGraphSchemaEdgeKindsWithSchemaName(ctx, edgeKindFilters, model.Sort{}, 0, 0); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

If you don't need the schema name here, you could save a JOIN with GetGraphSchemaEdgeKinds()

validKinds := graph.Kinds(ad.Relationships()).Concatenate(azure.Relationships())
// kindIsValidButIsNotTraversable determines if a kind exists in the built-in graph, but is not traversable.
func kindIsValidButIsNotTraversable(kind graph.Kind, onlyIncludeTraversableKinds bool) bool {
validBuiltInKinds := graph.Kinds(ad.Relationships()).Concatenate(azure.Relationships())
Copy link
Member

Choose a reason for hiding this comment

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

I'm confused by this part, is validBuiltInKinds a list of only traversable rels or all builtin rels? It looks like all builtin rels to me. A comment explaining would be good here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for working through this with me!

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/api/src/api/v2/pathfinding.go (1)

104-136: Unused onlyTraversable parameter.

The onlyTraversable parameter is passed to parseRelationshipKindsParam but never used within the function body. Either remove it or document its intended purpose.

🔧 Suggested fix: remove unused parameter
-func parseRelationshipKindsParam(acceptableKinds graph.Kinds, relationshipKindsParam string, onlyTraversable bool) (graph.Kinds, string, error) {
+func parseRelationshipKindsParam(acceptableKinds graph.Kinds, relationshipKindsParam string) (graph.Kinds, string, error) {

Then update callers at lines 147 and elsewhere to not pass the third argument.

🧹 Nitpick comments (3)
packages/cue/bh/ad/ad.cue (1)

1915-1977: Add documentation explaining the intentional differences between PathfindingRelationships and PathfindingRelationshipsMatchFrontend.

PathfindingRelationshipsMatchFrontend intentionally differs from PathfindingRelationships by:

  • Excluding ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO (which are in SharedRelationshipKinds)
  • Including ProtectAdminGroups (which is not in SharedRelationshipKinds)
  • Not including the additional edges from PathfindingRelationships (Contains, DCFor, SameForestTrust, SpoofSIDHistory, AbuseTGTDelegation)

Consider adding a comment explaining why these relationships are excluded from the frontend's traversable edges. Using an explicit list means future updates to SharedRelationshipKinds won't automatically propagate here, so documenting the rationale will help maintainers understand whether new relationships should be added to this list.

cmd/api/src/queries/graph_integration_test.go (1)

507-778: Comprehensive test coverage for OpenGraph pathfinding.

The test covers key scenarios well:

  1. OpenGraph-to-OpenGraph paths
  2. AD-to-AD paths
  3. Cross-graph (OpenGraph-to-AD) paths
  4. Edge kind filtering (exclude/include)

Minor formatting nit: Lines 672 and 741 have unnecessary blank lines after the Edges: []*graph.Relationship{ opening brace.

♻️ Optional: Remove extra blank lines
 				Edges: []*graph.Relationship{
-
 					{
 						ID:         1,
cmd/api/src/api/v2/pathfinding.go (1)

138-141: Consider caching the built-in kinds slice.

kindIsValidBuiltIn creates a new concatenated slice on every call. While the impact is minimal for typical usage, consider caching this at package level for efficiency.

♻️ Optional optimization
+var allBuiltInKinds = graph.Kinds(ad.Relationships()).Concatenate(azure.Relationships())
+
 // kindIsValidBuiltIn determines if a kind exists in the built-in graph
 func kindIsValidBuiltIn(kind graph.Kind) bool {
-	return graph.Kinds(ad.Relationships()).Concatenate(azure.Relationships()).ContainsOneOf(kind)
+	return allBuiltInKinds.ContainsOneOf(kind)
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1800ffb and c373ada.

📒 Files selected for processing (17)
  • cmd/api/src/api/constant.go
  • cmd/api/src/api/v2/pathfinding.go
  • cmd/api/src/api/v2/pathfinding_internal_test.go
  • cmd/api/src/api/v2/pathfinding_test.go
  • cmd/api/src/queries/fixtures/OpenGraphJSON/raw/base.json
  • cmd/api/src/queries/graph.go
  • cmd/api/src/queries/graph_integration_test.go
  • cmd/api/src/queries/mocks/graph.go
  • packages/cue/bh/ad/ad.cue
  • packages/cue/bh/bh.cue
  • packages/go/analysis/analysis.go
  • packages/go/graphschema/ad/ad.go
  • packages/go/openapi/doc/openapi.json
  • packages/go/openapi/src/paths/graph-schema.edge-kinds.yaml
  • packages/go/openapi/src/paths/graph.graphs.shortest-path.yaml
  • packages/go/schemagen/generator/golang.go
  • packages/go/schemagen/model/schema.go
💤 Files with no reviewable changes (1)
  • packages/go/openapi/src/paths/graph-schema.edge-kinds.yaml
🚧 Files skipped from review as they are similar to previous changes (7)
  • cmd/api/src/api/constant.go
  • packages/go/openapi/src/paths/graph.graphs.shortest-path.yaml
  • packages/cue/bh/bh.cue
  • cmd/api/src/api/v2/pathfinding_internal_test.go
  • packages/go/openapi/doc/openapi.json
  • packages/go/schemagen/model/schema.go
  • packages/go/schemagen/generator/golang.go
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-07-22T20:30:34.839Z
Learnt from: LawsonWillard
Repo: SpecterOps/BloodHound PR: 1700
File: cmd/api/src/api/v2/saved_queries_test.go:3182-3182
Timestamp: 2025-07-22T20:30:34.839Z
Learning: In Go table-driven tests in cmd/api/src/api/v2/saved_queries_test.go, subtest parallelization with t.Parallel() is acceptable when tests are self-contained, each creating their own mock controller (gomock.NewController(t)) and having isolated mock expectations without shared state between subtests.

Applied to files:

  • cmd/api/src/queries/graph_integration_test.go
  • cmd/api/src/api/v2/pathfinding_test.go
📚 Learning: 2025-11-05T21:13:08.542Z
Learnt from: ktstrader
Repo: SpecterOps/BloodHound PR: 2020
File: cmd/api/src/queries/graph.go:198-203
Timestamp: 2025-11-05T21:13:08.542Z
Learning: In cmd/api/src/queries/graph.go, when ETAC filtering is enabled in GetAssetGroupComboNode, an empty etacAllowedList (when user.AllEnvironments is false and user.EnvironmentTargetedAccessControl is empty) is intentional and should not be guarded against. The empty list will cause query.In() to filter out all nodes, which is the correct security behavior—users with no environment assignments should see no results.

Applied to files:

  • cmd/api/src/queries/graph.go
📚 Learning: 2025-07-09T00:36:54.112Z
Learnt from: mistahj67
Repo: SpecterOps/BloodHound 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.

Applied to files:

  • cmd/api/src/queries/graph.go
📚 Learning: 2025-11-25T22:11:53.518Z
Learnt from: LawsonWillard
Repo: SpecterOps/BloodHound PR: 2107
File: cmd/api/src/database/graphschema.go:86-100
Timestamp: 2025-11-25T22:11:53.518Z
Learning: In cmd/api/src/database/graphschema.go, the CreateSchemaEdgeKind method intentionally does not use AuditableTransaction or audit logging because it would create too much noise in the audit log, unlike CreateGraphSchemaExtension which does use auditing.

Applied to files:

  • cmd/api/src/api/v2/pathfinding.go
📚 Learning: 2025-06-06T23:12:14.181Z
Learnt from: elikmiller
Repo: SpecterOps/BloodHound PR: 1563
File: packages/go/graphschema/azure/azure.go:24-24
Timestamp: 2025-06-06T23:12:14.181Z
Learning: In BloodHound, files in packages/go/graphschema/*/`*.go` are generated from CUE schemas. When `just prepare-for-codereview` is run, it triggers code generation that may automatically add import aliases or other formatting changes. These changes are legitimate outputs of the generation process, not manual edits that would be overwritten.

Applied to files:

  • cmd/api/src/api/v2/pathfinding_test.go
  • packages/cue/bh/ad/ad.cue
📚 Learning: 2025-08-28T16:43:43.961Z
Learnt from: mvlipka
Repo: SpecterOps/BloodHound PR: 1784
File: packages/go/openapi/doc/openapi.json:18008-18029
Timestamp: 2025-08-28T16:43:43.961Z
Learning: In SpecterOps/BloodHound, packages/go/openapi/doc/openapi.json is generated from YAML under packages/go/openapi/src/schemas; edits must be made to the YAML and then the spec regenerated.

Applied to files:

  • cmd/api/src/api/v2/pathfinding_test.go
🧬 Code graph analysis (5)
packages/go/graphschema/ad/ad.go (2)
packages/go/graphschema/azure/azure.go (3)
  • Owns (72-72)
  • MemberOf (55-55)
  • Contains (49-49)
packages/go/ein/incoming_models.go (1)
  • GPLink (249-252)
packages/go/analysis/analysis.go (3)
packages/go/graphschema/ad/ad.go (2)
  • Entity (28-28)
  • Nodes (1146-1148)
packages/go/graphschema/common/common.go (2)
  • Nodes (33-35)
  • ObjectID (52-52)
cmd/api/src/model/filter.go (1)
  • Filter (479-483)
cmd/api/src/queries/graph_integration_test.go (3)
cmd/api/src/queries/graph.go (1)
  • NewGraphQuery (171-181)
packages/go/graphschema/graph.go (1)
  • Path (35-39)
packages/go/lab/generic/graph.go (1)
  • Node (36-40)
cmd/api/src/queries/graph.go (2)
packages/go/lab/generic/graph.go (2)
  • Node (36-40)
  • Graph (54-57)
packages/go/analysis/analysis.go (2)
  • FetchNodeByObjectID (98-108)
  • FetchNodeByObjectIDIncludeOpenGraph (110-119)
cmd/api/src/api/v2/pathfinding_test.go (3)
packages/go/graphschema/ad/ad.go (11)
  • Relationships (1149-1151)
  • PathfindingRelationshipsMatchFrontend (1161-1163)
  • PathfindingRelationships (1158-1160)
  • Contains (54-54)
  • Owns (44-44)
  • GenericAll (45-45)
  • GenericWrite (46-46)
  • Nodes (1146-1148)
  • Entity (28-28)
  • Computer (30-30)
  • User (29-29)
cmd/api/src/model/graphschema.go (1)
  • GraphSchemaEdgeKinds (86-86)
packages/go/graphschema/azure/azure.go (1)
  • ServicePrincipalEndpointReadWriteAll (84-84)
⏰ 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 (19)
packages/go/analysis/analysis.go (2)

88-93: LGTM - Well-designed predicate for OpenGraph node filtering.

The predicate correctly excludes both ad.Entity and azure.Entity kinds to identify OpenGraph nodes while matching by the indexed property.


110-119: LGTM - Clean fallback pattern for OpenGraph node lookup.

The function properly:

  1. Attempts the standard AD/Azure lookup first
  2. Distinguishes "not found" errors from actual failures
  3. Falls back to OpenGraph query only when the node isn't found in standard entities

This maintains backward compatibility while extending support for OpenGraph nodes.

packages/cue/bh/ad/ad.cue (1)

31-31: LGTM - Export declaration for new pathfinding relationship set.

packages/go/graphschema/ad/ad.go (1)

1161-1163: LGTM - Generated code matches CUE schema.

This generated function correctly reflects the PathfindingRelationshipsMatchFrontend declaration from the CUE schema. Based on learnings, this file is auto-generated from CUE schemas when just prepare-for-codereview is run.

cmd/api/src/queries/graph.go (3)

139-139: LGTM - Interface extended for OpenGraph support.

The new method signature matches the existing GetAllShortestPaths pattern, maintaining API consistency.


251-279: LGTM - Clean refactoring with pluggable node fetcher.

The internal helper effectively abstracts the node-fetching strategy via the nodeFetcher parameter, enabling code reuse between standard and OpenGraph-aware path lookups. The implementation correctly:

  • Applies the filter criteria when provided
  • Handles errors from node fetching
  • Collects only paths with edges (maintaining existing behavior)

281-289: LGTM - Public methods correctly delegate to internal helper.

Both methods appropriately use the measurement decorator and delegate to getAllShortestPathsInternal with the correct node fetcher:

  • GetAllShortestPaths: Uses analysis.FetchNodeByObjectID (AD/Azure only)
  • GetAllShortestPathsWithOpenGraph: Uses analysis.FetchNodeByObjectIDIncludeOpenGraph (includes OpenGraph nodes)
cmd/api/src/queries/mocks/graph.go (1)

199-212: LGTM - Generated mock matches interface.

The mock implementation and recorder method correctly follow the established pattern and match the new GetAllShortestPathsWithOpenGraph interface method signature.

cmd/api/src/queries/graph_integration_test.go (2)

139-139: LGTM!

The change from "TEST-1" to "2" aligns the test with the updated object-id-based identifiers used in the fixture data.


226-226: LGTM!

Consistent with the previous change, using numeric object IDs for exact-match scenarios.

cmd/api/src/api/v2/pathfinding.go (4)

143-155: LGTM!

The helper correctly centralizes filter criteria creation. Minor nit: extra blank line at line 154.


169-172: Verify error handling for invalid boolean parameter.

When ParseOptionalBool fails, the error is logged but execution continues with false as the default. Confirm this is the intended behavior—it means clients passing invalid values like "notABool" get silent fallback to false rather than a 400 response.

The test at line 573-597 in pathfinding_test.go confirms this is intentional behavior.


214-222: LGTM!

Clean helper function with proper context usage and error handling.


224-247: LGTM!

The OpenGraph path retrieval logic correctly:

  1. Builds traversable filters when onlyIncludeTraversableKinds is true
  2. Fetches OpenGraph edge kinds from the database
  3. Extends valid kinds with OpenGraph edges
  4. Creates the appropriate filter criteria

Minor formatting nit: extra blank lines at 241 and 246-247.

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

107-122: Well-organized test fixtures.

The test setup clearly defines the different kind sets and filters needed for various scenarios:

  • Built-in kinds (all vs traversable)
  • OpenGraph kinds
  • Combined kinds with OpenGraph
  • Pre-built filter criteria for assertions

This makes the test cases more readable and maintainable.


573-648: Good coverage for only_traversable parameter behavior.

These tests verify:

  1. Invalid boolean values default to false (all kinds filter)
  2. only_traversable=true uses traversable kinds filter
  3. only_traversable=true with relationship_kinds filters to intersection of traversable and requested kinds

The test at line 639 correctly expects only ad.Contains since AZScopedTo is not in the traversable set.


956-1036: Comprehensive OpenGraph traversable filtering tests.

These tests verify the interaction between only_traversable and OpenGraph:

  1. Line 970: DB is queried with is_traversable filter
  2. Line 972: Path query uses traversable kinds with OpenGraph
  3. Lines 982-1008: IN operator with traversable filtering
  4. Lines 1010-1036: NIN operator with traversable filtering

The mock expectations correctly verify the implementation behavior.

cmd/api/src/queries/fixtures/OpenGraphJSON/raw/base.json (2)

88-165: Edges fixture looks consistent with node IDs.

match_by: "id" references align with existing node IDs and the edge kinds should exercise pathfinding well.


6-85: No action needed. The fixture is correctly structured. Node IDs in the fixture ("id": "7", "id": "2", etc.) are properly converted to objectid properties when written to the database via WriteGraphToDatabase, which explicitly sets objectid = node.ID. Test assertions expecting "objectid": "7" match the fixture's "id": "7" and will work as designed. No consumer updates required.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Member

@AD7ZJ AD7ZJ left a comment

Choose a reason for hiding this comment

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

One thought, otherwise it looks good! It's working nicely on my local environment.

PathfindingRelationships: list.Concat([SharedRelationshipKinds,[Contains, DCFor, SameForestTrust, SpoofSIDHistory, AbuseTGTDelegation]])

// Edges that are used in Shortest Path and match the frontend's list of traversable edges
PathfindingRelationshipsMatchFrontend: [
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a way with CUE to define this list in relation to the existing PathfindingRelationships list? Possibly using the list.Concat method in conjunction with a filter for the 4 edges we don't want?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll try that, thanks for the suggestion!

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