Skip to content

Bug: packages/go/analysis/ad/ad.go:GetEdgeCompositionPath – No default case for unknown edge kinds, causing silent path loss #2709

@xmp00

Description

@xmp00

Description:

When GetEdgeCompositionPath in packages/go/analysis/ad/ad.go receives an edge of an unknown kind, the switch block has no default case. The function exits with an empty path set and a nil error, silently dropping data. If a new AD edge kind is added to the BloodHound schema but not registered in this function, no error is surfaced-attack paths are simply missing with no indication.

Are you intending to fix this bug?

No

Component(s) Affected:

AD analysis package: packages/go/analysis/ad

Steps to Reproduce:

  1. Open packages/go/analysis/ad/ad.go and locate the GetEdgeCompositionPath function.
  2. In a unit test or using a debugger, call GetEdgeCompositionPath with a graph.Relationship whose Kind is not one of the explicitly listed cases.
  3. Observe that the returned PathSet is empty and the error is nil.
  4. Verify that the default branch is missing from the switch statement.

Expected Behavior:

The function should return an error that clearly states “no edge composition handler for kind X”, failing loudly instead of silently.

Actual Behavior:

The function returns (empty path set, nil), making it impossible to distinguish between an edge that legitimately has no composition and a programming oversight.

Screenshots/Code Snippets/Sample Files:

Current code:
func GetEdgeCompositionPath(ctx context.Context, db graph.Database, edge *graph.Relationship) (graph.PathSet, error) {
var (
err error
pathSet = graph.NewPathSet()
)
if err = db.ReadTransaction(ctx, func(tx graph.Transaction) error {
switch edge.Kind {
case ad.GoldenCert:
pathSet, err = getGoldenCertEdgeComposition(tx, edge)
case ad.ADCSESC1:
pathSet, err = GetADCSESC1EdgeComposition(ctx, db, edge)
// … (other cases) …
case ad.CoerceAndRelayNTLMToSMB:
pathSet, err = GetCoerceAndRelayNTLMtoSMBEdgeComposition(ctx, db, edge)
}
return err
}); err != nil {
return graph.NewPathSet(), err
}
return pathSet, nil
}

Environment Information:

BloodHound: latest main (as of 2026-04-26)
OS: Kali
Go (API): 1.22 (or whatever version go.mod specifies)
Database: N/A
Docker: N/A

Additional Information:

NA

Potential Solution (optional):

Add a default case that returns a descriptive error, and add "fmt" to the imports.

import (
"context"
"fmt"

"github.com/specterops/bloodhound/analysis"
"github.com/specterops/bloodhound/analysis/ad"
"github.com/specterops/bloodhound/dawgs/graph"

)

func GetEdgeCompositionPath(...) (...) {
...
switch edge.Kind {
case ad.GoldenCert:
pathSet, err = getGoldenCertEdgeComposition(tx, edge)
// ... all existing cases ...
case ad.CoerceAndRelayNTLMToSMB:
pathSet, err = GetCoerceAndRelayNTLMtoSMBEdgeComposition(ctx, db, edge)
default:
return fmt.Errorf("no edge composition handler for kind %s", edge.Kind.String())
}
return err
...
}

Related Issues:

None found.

Contributor Checklist:

  • I have searched the issue tracker to ensure this bug hasn't been reported before or is not already being addressed.
  • I have provided clear steps to reproduce the issue.
  • I have included relevant environment information details.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageThis issue requires triaging

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions