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:
- Open packages/go/analysis/ad/ad.go and locate the GetEdgeCompositionPath function.
- In a unit test or using a debugger, call GetEdgeCompositionPath with a graph.Relationship whose Kind is not one of the explicitly listed cases.
- Observe that the returned PathSet is empty and the error is nil.
- 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.
Description:
When
GetEdgeCompositionPathinpackages/go/analysis/ad/ad.goreceives an edge of an unknown kind, theswitchblock has nodefaultcase. 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:
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"
)
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: