Skip to content

chore: move finding-trends to new arch BED-9018 - #3101

Open
Flake85 wants to merge 5 commits into
mainfrom
BED-9018
Open

chore: move finding-trends to new arch BED-9018#3101
Flake85 wants to merge 5 commits into
mainfrom
BED-9018

Conversation

@Flake85

@Flake85 Flake85 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Description

Describe your changes in detail
Moved finding-trends to the new architecture.

Motivation and Context

Resolves BED-9018

Why is this change required? What problem does it solve?

How Has This Been Tested?

Please describe in detail how you tested your changes.
Include details of your testing environment, and the tests you ran to
see how your change affects other areas of the code, etc.

Screenshots (optional):

Types of changes

  • Chore (a change that does not modify the application functionality)

Checklist:

Summary by CodeRabbit

  • New Features
    • Added asset group tag lookup with validation, Tier Zero fallback, and hygiene-tag support.
    • Added graph node retrieval by object ID and kind.
    • Added feature flag lookup by key.
    • Added schema environment retrieval with optional built-in-only filtering.
    • Added access to environment kinds and associated schema metadata.
  • Tests
    • Added coverage for successful lookups, filtering, fallback behavior, missing data, and error handling.

@Flake85 Flake85 self-assigned this Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds PostgreSQL-backed asset-group tag resolution, graph node lookup, and OpenGraph schema environment lookup. It also exposes public adapters and extends feature-flag interfaces with GetFlagByKey. Tests and generated mocks cover the new contracts and implementations.

Changes

Asset group tag resolution

Layer / File(s) Summary
Tag resolution service
server/assetgroups/internal/services/*, server/assetgroups/internal/services/mocks/*
Defines asset-group tag types and errors. Resolves explicit IDs, the hygiene placeholder, and tier-zero fallback IDs.
Tag database access
server/assetgroups/internal/appdb/*
Retrieves active tags and the tier-zero tag from PostgreSQL. Unit and integration tests cover successful and missing-result paths.
Public adapter wiring
server/assetgroups/assetgroups.go, server/assetgroups/mocks/*
Exposes the adapter contract and constructs the store-backed service. Adds generated adapter mocks.

Graph node lookup

Layer / File(s) Summary
Node fetch implementation
server/graphdb/internal/appdb/*, server/graphdb/internal/services/*
Fetches nodes by object IDs and kinds through a graph read transaction. Service and integration tests cover forwarding, filtering, and errors.
Graph adapter wiring
server/graphdb/graphdb.go, server/graphdb/mocks/*
Exposes the graph request adapter and wires the application store into the graph service. Adds variadic-call mock support.

OpenGraph schema environment lookup

Layer / File(s) Summary
Environment database access
server/opengraphschema/internal/appdb/*
Queries schema environments, joins kind and extension metadata, and optionally filters builtin extensions. Unit and integration tests cover both modes.
Environment kind conversion
server/opengraphschema/internal/services/*
Converts database environments into graph kinds and an environment map while propagating database errors.
Public schema adapter wiring
server/opengraphschema/opengraphschema.go, server/opengraphschema/mocks/*
Exposes the schema adapter and constructs the store-backed service. Adds generated adapter mocks.

Feature flag lookup

Layer / File(s) Summary
GetFlagByKey contract and handler
server/featureflags/featureflags.go, server/featureflags/internal/handlers/*, server/featureflags/mocks/*
Adds GetFlagByKey to the adapter and service boundaries. The handler forwards the context and key without transformation. Generated mocks support typed returns and callbacks.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant AssetGroupsRequestAdapter
  participant Service
  participant Store
  participant PostgreSQL
  Caller->>AssetGroupsRequestAdapter: ResolveTagIDsWithFallback
  AssetGroupsRequestAdapter->>Service: ResolveTagIDsWithFallback
  Service->>Store: GetAssetGroupTagByID or GetTierZeroTag
  Store->>PostgreSQL: Query active asset-group tags
  PostgreSQL-->>Store: Return tag rows
  Store-->>Service: Return AssetGroupTag
  Service-->>Caller: Return resolved tag IDs
Loading

Possibly related PRs

Suggested labels: infrastructure, go

Suggested reviewers: urangel

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: moving finding-trends to the new architecture under BED-9018.
Description check ✅ Passed The description identifies the migration, ticket, change type, and checklist status, but it omits detailed motivation and testing results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch BED-9018

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

@Flake85
Flake85 marked this pull request as ready for review August 6, 2026 15:55
@coderabbitai coderabbitai Bot added go Pull requests that update go code infrastructure A pull request containing changes affecting the infrastructure code. labels Aug 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
server/assetgroups/internal/services/services.go (1)

35-41: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use descriptive database field names consistently.

Both stores use the abbreviated field name db. Rename each field to database and update its method reads.

  • server/assetgroups/internal/services/services.go#L35-L41: rename Service.db to Service.database.
  • server/assetgroups/internal/appdb/store.go#L39-L45: rename Store.db to Store.database.

As per coding guidelines, prefer descriptive variable names, such as databaseInterface, instead of abbreviated names such as di or dbi.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/assetgroups/internal/services/services.go` around lines 35 - 41,
Rename Service.db to Service.database in
server/assetgroups/internal/services/services.go and update all Service method
references. Rename Store.db to Store.database in
server/assetgroups/internal/appdb/store.go and update all Store method
references; retain descriptive constructor parameter names such as
databaseInterface.

Source: Coding guidelines

server/assetgroups/assetgroups.go (1)

30-41: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Expose the asset-group not-found errors from the public package.

AssetGroupsRequestAdapter propagates both sentinels, but external consumers cannot use errors.Is without importing the internal package. Add public aliases for ErrAssetGroupTagNotFound and ErrTierZeroTagNotFound.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/assetgroups/assetgroups.go` around lines 30 - 41, Expose public
aliases for services.ErrAssetGroupTagNotFound and
services.ErrTierZeroTagNotFound alongside AssetGroupTag and
AssetGroupsRequestAdapter, preserving their identity so external consumers can
use errors.Is without importing the internal package.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/featureflags/featureflags.go`:
- Line 41: Update FeatureFlagRequestAdapter’s GetFlagByKey contract to return a
FeatureFlag type from a public package instead of services.FeatureFlag. Define
the public model and translate the service-layer value at the adapter boundary,
updating the public mock and Return signatures to use the new type.

---

Nitpick comments:
In `@server/assetgroups/assetgroups.go`:
- Around line 30-41: Expose public aliases for services.ErrAssetGroupTagNotFound
and services.ErrTierZeroTagNotFound alongside AssetGroupTag and
AssetGroupsRequestAdapter, preserving their identity so external consumers can
use errors.Is without importing the internal package.

In `@server/assetgroups/internal/services/services.go`:
- Around line 35-41: Rename Service.db to Service.database in
server/assetgroups/internal/services/services.go and update all Service method
references. Rename Store.db to Store.database in
server/assetgroups/internal/appdb/store.go and update all Store method
references; retain descriptive constructor parameter names such as
databaseInterface.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: e5b77b2d-56b3-480b-affb-83f0c2caa5c1

📥 Commits

Reviewing files that changed from the base of the PR and between c9e4e36 and 64a6398.

📒 Files selected for processing (29)
  • server/assetgroups/assetgroups.go
  • server/assetgroups/internal/appdb/store.go
  • server/assetgroups/internal/appdb/tag.go
  • server/assetgroups/internal/appdb/tag_integration_test.go
  • server/assetgroups/internal/appdb/tag_test.go
  • server/assetgroups/internal/services/mocks/database.go
  • server/assetgroups/internal/services/services.go
  • server/assetgroups/internal/services/tag.go
  • server/assetgroups/internal/services/tag_test.go
  • server/assetgroups/mocks/assetgroupsrequestadapter.go
  • server/featureflags/featureflags.go
  • server/featureflags/internal/handlers/handlers.go
  • server/featureflags/internal/handlers/mocks/featureflag.go
  • server/featureflags/mocks/featureflagrequestadapter.go
  • server/graphdb/graphdb.go
  • server/graphdb/internal/appdb/node.go
  • server/graphdb/internal/appdb/node_integration_test.go
  • server/graphdb/internal/services/mocks/database.go
  • server/graphdb/internal/services/node_test.go
  • server/graphdb/internal/services/services.go
  • server/graphdb/mocks/graphdbrequestadapter.go
  • server/opengraphschema/internal/appdb/store.go
  • server/opengraphschema/internal/appdb/store_integration_test.go
  • server/opengraphschema/internal/appdb/store_test.go
  • server/opengraphschema/internal/services/mocks/database.go
  • server/opengraphschema/internal/services/services.go
  • server/opengraphschema/internal/services/services_test.go
  • server/opengraphschema/mocks/opengraphschemarequestadapter.go
  • server/opengraphschema/opengraphschema.go


type FeatureFlagRequestAdapter interface {
IsEnabled(ctx context.Context, key string) (bool, error)
GetFlagByKey(ctx context.Context, key string) (services.FeatureFlag, error)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline server/featureflags/featureflags.go --items all

rg -n -C 4 --type go \
  'FeatureFlagRequestAdapter|NewFeatureFlagRequestAdapter|GetFlagByKey\s*\(|MockFeatureFlagRequestAdapter|EXPECT\(\)\.GetFlagByKey' .

rg -n -C 3 --type go \
  '"github\.com/specterops/bloodhound/server/featureflags/internal/services"' .

Repository: SpecterOps/BloodHound

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- adapter and type definitions ---'
sed -n '1,90p' server/featureflags/featureflags.go
sed -n '1,150p' server/featureflags/internal/services/services.go
sed -n '1,140p' server/featureflags/mocks/featureflagrequestadapter.go

printf '%s\n' '--- module and external adapter usage ---'
sed -n '1,40p' go.mod
rg -n --type go \
  'featureflags\.(FeatureFlagRequestAdapter|NewFeatureFlagRequestAdapter)|mocks\.NewMockFeatureFlagRequestAdapter|FeatureFlagRequestAdapter' \
  --glob '!server/featureflags/**' .

printf '%s\n' '--- minimal Go internal-package API probe ---'
probe_dir="$(mktemp -d)"
trap 'rm -rf "$probe_dir"' EXIT
mkdir -p "$probe_dir/parent/internal/model" "$probe_dir/parent/api" "$probe_dir/client"
cat >"$probe_dir/go.mod" <<'EOF'
module example.com/probe

go 1.22
EOF
cat >"$probe_dir/parent/internal/model/model.go" <<'EOF'
package model
type Value struct{ Enabled bool }
EOF
cat >"$probe_dir/parent/api/api.go" <<'EOF'
package api
import "example.com/probe/parent/internal/model"
type Provider interface {
	Get() (model.Value, error)
}
EOF
cat >"$probe_dir/client/main.go" <<'EOF'
package main
import "example.com/probe/parent/api"
type implementation struct{}
func (implementation) Get() (struct{ Enabled bool }, error) { return struct{ Enabled bool }{}, nil }
var _ api.Provider = implementation{}
func main() {}
EOF
(
  cd "$probe_dir"
  if go test ./client 2>&1; then
    echo 'external implementation unexpectedly compiled'
  else
    echo 'external implementation did not compile, as expected'
  fi
)
 
cat >"$probe_dir/client/main.go" <<'EOF'
package main
import "example.com/probe/parent/api"
func main() {
	var provider api.Provider
	_, _ = provider.Get()
}
EOF
(
  cd "$probe_dir"
  if go test ./client 2>&1; then
    echo 'external method call compiled'
  else
    echo 'external method call did not compile'
  fi
)

Repository: SpecterOps/BloodHound

Length of output: 12575


🏁 Script executed:

#!/bin/bash
set -u

printf '%s\n' '--- package declarations and direct imports ---'
rg -n --type go \
  '^(package |[[:space:]]*"github\.com/specterops/bloodhound/server/featureflags"|[[:space:]]*featureflags "[^"]+")' \
  server/featureflags cmd packages \
  | head -200

printf '%s\n' '--- minimal Go internal-package API probe ---'
probe_dir="$(mktemp -d)"
trap 'rm -rf "$probe_dir"' EXIT
mkdir -p "$probe_dir/parent/internal/model" "$probe_dir/parent/api" "$probe_dir/client"

cat >"$probe_dir/go.mod" <<'EOF'
module example.com/probe

go 1.22
EOF
cat >"$probe_dir/parent/internal/model/model.go" <<'EOF'
package model
type Value struct{ Enabled bool }
EOF
cat >"$probe_dir/parent/api/api.go" <<'EOF'
package api
import "example.com/probe/parent/internal/model"
type Provider interface {
	Get() (model.Value, error)
}
func New() Provider { return provider{} }
type provider struct{}
func (provider) Get() (model.Value, error) { return model.Value{}, nil }
EOF

printf '%s\n' 'case 1: external package calls method without naming internal type'
cat >"$probe_dir/client/main.go" <<'EOF'
package main
import "example.com/probe/parent/api"
func main() {
	var provider api.Provider = api.New()
	value, err := provider.Get()
	_ = value
	_ = err
}
EOF
(cd "$probe_dir" && go test ./client)
printf '%s\n' 'case 1: compiled'

printf '%s\n' 'case 2: external package implements interface with structurally similar type'
cat >"$probe_dir/client/main.go" <<'EOF'
package main
import "example.com/probe/parent/api"
type implementation struct{}
func (implementation) Get() (struct{ Enabled bool }, error) {
	return struct{ Enabled bool }{}, nil
}
var _ api.Provider = implementation{}
func main() {}
EOF
if (cd "$probe_dir" && go test ./client); then
  echo 'case 2: unexpectedly compiled'
else
  echo 'case 2: failed as expected'
fi

printf '%s\n' 'case 3: external package imports the internal package directly'
cat >"$probe_dir/client/main.go" <<'EOF'
package main
import (
	"example.com/probe/parent/api"
	"example.com/probe/parent/internal/model"
)
var _ model.Value
var _ api.Provider
func main() {}
EOF
if (cd "$probe_dir" && go test ./client); then
  echo 'case 3: unexpectedly compiled'
else
  echo 'case 3: failed as expected'
fi

Repository: SpecterOps/BloodHound

Length of output: 13161


Do not expose services.FeatureFlag in FeatureFlagRequestAdapter.

External packages can call the method, but they cannot import the internal type or implement the interface with a compatible declared return type. The public mock also requires this internal type in Return. Define FeatureFlag in a public package and translate the service value at the adapter boundary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/featureflags/featureflags.go` at line 41, Update
FeatureFlagRequestAdapter’s GetFlagByKey contract to return a FeatureFlag type
from a public package instead of services.FeatureFlag. Define the public model
and translate the service-layer value at the adapter boundary, updating the
public mock and Return signatures to use the new type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go Pull requests that update go code infrastructure A pull request containing changes affecting the infrastructure code.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant