Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3614df6
BED-6721: generate sql
wes-mil Dec 17, 2025
51bf581
BED-6721: make it work more good
wes-mil Dec 18, 2025
02f4c45
fk node and edge schema kind tables to DAWGS kind table
LawsonWillard Dec 19, 2025
fac4895
update doc strings
LawsonWillard Dec 19, 2025
66e7063
update doc strings
LawsonWillard Dec 19, 2025
b6bdad8
update id to small int and update table doc strings
LawsonWillard Dec 19, 2025
33f0fc7
remove feature flag
LawsonWillard Dec 19, 2025
caa0289
clarify doc strings
LawsonWillard Dec 19, 2025
7c6b164
BED-6721: run data population
wes-mil Dec 22, 2025
a7e1e2a
BED-6721: abstract function and rename files
wes-mil Jan 5, 2026
f349c99
Merge branch 'main' into BED-6721
wes-mil Jan 5, 2026
91c5b4a
BED-6721: fix az schema, run just generate
wes-mil Jan 5, 2026
4291d8f
BED-6721: add insert statements
wes-mil Jan 6, 2026
875a397
BED-6721: revert code gen
wes-mil Jan 6, 2026
ea47bf0
BED-6721: fix license year
wes-mil Jan 6, 2026
4a30bee
Merge branch 'main' into BED-6721
wes-mil Jan 6, 2026
c0cb2a5
Merge branch 'main' into BED-7067
LawsonWillard Jan 8, 2026
4225986
add postgres functions to insert node and edge kinds
LawsonWillard Jan 8, 2026
6658710
update graph schema functions to use new postgres functions
LawsonWillard Jan 8, 2026
a76497f
update tests to ensure node and edge kinds cannot share the same DAWG…
LawsonWillard Jan 8, 2026
31daabf
wip
LawsonWillard Jan 8, 2026
4c2a13d
Merge branch 'main' into BED-7067
LawsonWillard Jan 9, 2026
fd3b84e
add table lock to insert node and edge kinds
LawsonWillard Jan 9, 2026
10df74b
Merge branch 'main' into BED-7067
LawsonWillard Jan 12, 2026
81fe5b8
Update graph schema node and edge functions and tables to use a norma…
LawsonWillard Jan 12, 2026
babaf4e
ensure map assignments work
LawsonWillard Jan 12, 2026
303e1c2
add more documentation
LawsonWillard Jan 12, 2026
6488e3c
update filtering and sorting to include table identifiers
LawsonWillard Jan 12, 2026
80c0082
Merge branch 'refs/heads/main' into BED-7067
LawsonWillard Jan 13, 2026
75a8207
update parameter test to conform with whats in migration
LawsonWillard Jan 13, 2026
d566a47
update node and edge schema tables to use kind id as foriegn key
LawsonWillard Jan 13, 2026
3fbbfc8
Merge branch 'main' into BED-7067
LawsonWillard Jan 13, 2026
9de9b4b
Merge branch 'BED-7067' into BED-6721
wes-mil Jan 13, 2026
7875630
BED-6721: update for BED-7067
wes-mil Jan 13, 2026
3cb6913
Merge branch 'main' into BED-6721
wes-mil Jan 14, 2026
3a481bb
BED-6721: this is the worst code i have ever written
wes-mil Jan 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cmd/api/src/bootstrap/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ func MigrateDB(ctx context.Context, cfg config.Configuration, db database.Databa
return CreateDefaultAdmin(ctx, cfg, db, defaultAdminFunc)
}

func PopulateExtensionData(ctx context.Context, db database.Database) error {
if err := db.PopulateExtensionData(ctx); err != nil {
return err
}

return nil
}

func CreateDefaultAdmin(ctx context.Context, cfg config.Configuration, db database.Database, defaultAdminFunction func() (config.DefaultAdminConfiguration, error)) error {
var (
secretDigester = cfg.Crypto.Argon2.NewDigester()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func setupIntegrationTest(t *testing.T) IntegrationTestSuite {

db := database.NewBloodhoundDB(gormDB, auth.NewIdentityResolver())
require.NoError(t, db.Migrate(ctx))
require.NoError(t, db.PopulateExtensionData(ctx))

return IntegrationTestSuite{
Context: ctx,
Expand Down
3 changes: 3 additions & 0 deletions cmd/api/src/daemons/datapipe/datapipe_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ func setupIntegrationTestSuite(t *testing.T, fixturesPath string) IntegrationTes
err = graphDB.AssertSchema(ctx, graphschema.DefaultGraphSchema())
require.NoError(t, err)

err = db.PopulateExtensionData(ctx)
require.NoError(t, err)

ingestSchema, err := upload.LoadIngestSchema()
require.NoError(t, err)

Expand Down
3 changes: 3 additions & 0 deletions cmd/api/src/database/database_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ func setupIntegrationTestSuite(t *testing.T) IntegrationTestSuite {
err = db.Migrate(ctx)
require.NoError(t, err)

err = db.PopulateExtensionData(ctx)
require.NoError(t, err)

// #endregion

return IntegrationTestSuite{
Expand Down
13 changes: 12 additions & 1 deletion cmd/api/src/database/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/specterops/bloodhound/cmd/api/src/services/agi"
"github.com/specterops/bloodhound/cmd/api/src/services/dataquality"
"github.com/specterops/bloodhound/cmd/api/src/services/upload"
"github.com/specterops/bloodhound/packages/go/bhlog/attr"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
Expand Down Expand Up @@ -99,6 +100,7 @@ type Database interface {

Wipe(ctx context.Context) error
Migrate(ctx context.Context) error
PopulateExtensionData(ctx context.Context) error
CreateInstallation(ctx context.Context) (model.Installation, error)
GetInstallation(ctx context.Context) (model.Installation, error)
HasInstallation(ctx context.Context) (bool, error)
Expand Down Expand Up @@ -280,7 +282,16 @@ func (s *BloodhoundDB) Wipe(ctx context.Context) error {
func (s *BloodhoundDB) Migrate(ctx context.Context) error {
// Run the migrator
if err := migration.NewMigrator(s.db.WithContext(ctx)).ExecuteStepwiseMigrations(); err != nil {
slog.ErrorContext(ctx, fmt.Sprintf("Error during SQL database migration phase: %v", err))
slog.ErrorContext(ctx, "Error during SQL database migration phase", attr.Error(err))
return err
}

return nil
}

func (s *BloodhoundDB) PopulateExtensionData(ctx context.Context) error {
if err := migration.NewMigrator(s.db.WithContext(ctx)).ExecuteExtensionDataPopulation(); err != nil {
slog.ErrorContext(ctx, "Error during extensions data population phase", attr.Error(err))
return err
}

Expand Down
68 changes: 61 additions & 7 deletions cmd/api/src/database/graphschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func (s *BloodhoundDB) CreateGraphSchemaNodeKind(ctx context.Context, name strin
if strings.Contains(result.Error.Error(), DuplicateKeyValueErrorString) {
return model.GraphSchemaNodeKind{}, fmt.Errorf("%w: %v", ErrDuplicateSchemaNodeKindName, result.Error)
}
return model.GraphSchemaNodeKind{}, CheckError(result)
return model.GraphSchemaNodeKind{}, result.Error
}
return schemaNodeKind, nil
}
Expand All @@ -232,11 +232,38 @@ func (s *BloodhoundDB) CreateGraphSchemaNodeKind(ctx context.Context, name strin
// populated with data, as well as an integer indicating the total number of rows returned by the query (excluding any given pagination).
func (s *BloodhoundDB) GetGraphSchemaNodeKinds(ctx context.Context, filters model.Filters, sort model.Sort, skip, limit int) (model.GraphSchemaNodeKinds, int, error) {
var (
schemaNodeKinds = model.GraphSchemaNodeKinds{}
totalRowCount int
schemaNodeKinds = model.GraphSchemaNodeKinds{}
totalRowCount int
tableIdentifiedFilters = make(model.Filters, len(filters))
tableIdentifiedSort = make(model.Sort, len(sort))
)

if filterAndPagination, err := parseFiltersAndPagination(filters, sort, skip, limit); err != nil {
// add table identifiers to filtering and sorting columns ensuring we don't return an ambiguous column error
for column, filter := range filters {
if column == "name" {
tableIdentifiedFilters[fmt.Sprintf("%s.%s", "k", column)] = filter
delete(filters, column)
} else {
tableIdentifiedFilters[fmt.Sprintf("%s.%s", "nk", column)] = filter
delete(filters, column)
}
}

for idx, sortItem := range sort {
if sort[idx].Column == "name" {
tableIdentifiedSort[idx] = model.SortItem{
Direction: sortItem.Direction,
Column: fmt.Sprintf("%s.%s", "k", sort[idx].Column),
}
} else {
tableIdentifiedSort[idx] = model.SortItem{
Direction: sortItem.Direction,
Column: fmt.Sprintf("%s.%s", "nk", sort[idx].Column),
}
}
}

if filterAndPagination, err := parseFiltersAndPagination(tableIdentifiedFilters, tableIdentifiedSort, skip, limit); err != nil {
return schemaNodeKinds, 0, err
} else {
sqlStr := fmt.Sprintf(`SELECT nk.id, k.name, nk.schema_extension_id, nk.display_name, nk.description,
Expand Down Expand Up @@ -450,11 +477,38 @@ func (s *BloodhoundDB) CreateGraphSchemaEdgeKind(ctx context.Context, name strin
// populated with data, as well as an integer indicating the total number of rows returned by the query (excluding any given pagination).
func (s *BloodhoundDB) GetGraphSchemaEdgeKinds(ctx context.Context, edgeKindFilters model.Filters, sort model.Sort, skip, limit int) (model.GraphSchemaEdgeKinds, int, error) {
var (
schemaEdgeKinds = model.GraphSchemaEdgeKinds{}
totalRowCount int
schemaEdgeKinds = model.GraphSchemaEdgeKinds{}
totalRowCount int
tableIdentifiedFilters = make(model.Filters, len(edgeKindFilters))
tableIdentifiedSort = make(model.Sort, len(sort))
)

if filterAndPagination, err := parseFiltersAndPagination(edgeKindFilters, sort, skip, limit); err != nil {
// add table identifiers to filtering and sorting columns ensuring we don't return an ambiguous column error
for column, filters := range edgeKindFilters {
if column == "name" {
tableIdentifiedFilters[fmt.Sprintf("%s.%s", "k", column)] = filters
delete(edgeKindFilters, column)
} else {
tableIdentifiedFilters[fmt.Sprintf("%s.%s", "ek", column)] = filters
delete(edgeKindFilters, column)
}
}

for idx, sortItem := range sort {
if sort[idx].Column == "name" {
tableIdentifiedSort[idx] = model.SortItem{
Direction: sortItem.Direction,
Column: fmt.Sprintf("%s.%s", "k", sort[idx].Column),
}
} else {
tableIdentifiedSort[idx] = model.SortItem{
Direction: sortItem.Direction,
Column: fmt.Sprintf("%s.%s", "ek", sort[idx].Column),
}
}
}

if filterAndPagination, err := parseFiltersAndPagination(tableIdentifiedFilters, tableIdentifiedSort, skip, limit); err != nil {
return schemaEdgeKinds, 0, err
} else {
sqlStr := fmt.Sprintf(`SELECT ek.id, k.name, ek.schema_extension_id, ek.description, ek.is_traversable,
Expand Down
4 changes: 2 additions & 2 deletions cmd/api/src/database/graphschema_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ func TestDatabase_GraphSchemaNodeKind_CRUD(t *testing.T) {
t.Run("fail - return error for filtering on non-existent column", func(t *testing.T) {
_, _, err = testSuite.BHDatabase.GetGraphSchemaNodeKinds(testSuite.Context,
model.Filters{"nonexistentcolumn": []model.Filter{{Operator: model.Equals, Value: "blah", SetOperator: model.FilterAnd}}}, model.Sort{}, 0, 0)
require.EqualError(t, err, "ERROR: column \"nonexistentcolumn\" does not exist (SQLSTATE 42703)")
require.EqualError(t, err, "ERROR: column nk.nonexistentcolumn does not exist (SQLSTATE 42703)")
})

// UPDATE
Expand Down Expand Up @@ -896,7 +896,7 @@ func TestDatabase_GraphSchemaEdgeKind_CRUD(t *testing.T) {
t.Run("fail - return error for filtering on non-existent column", func(t *testing.T) {
_, _, err = testSuite.BHDatabase.GetGraphSchemaEdgeKinds(testSuite.Context,
model.Filters{"nonexistentcolumn": []model.Filter{{Operator: model.Equals, Value: "blah", SetOperator: model.FilterAnd}}}, model.Sort{}, 0, 0)
require.EqualError(t, err, "ERROR: column \"nonexistentcolumn\" does not exist (SQLSTATE 42703)")
require.EqualError(t, err, "ERROR: column ek.nonexistentcolumn does not exist (SQLSTATE 42703)")
})

// UPDATE
Expand Down
Loading