Skip to content
Draft
90 changes: 90 additions & 0 deletions .github/workflows/code-generation-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Copyright 2025 Specter Ops, Inc.
#
# Licensed under the Apache License, Version 2.0
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0

name: Code Generation Check

on:
push:
branches:
- BED-6802

jobs:
code-generation-check:
runs-on: ubuntu-latest

steps:
- name: Checkout source code for this repository
uses: actions/checkout@v4
with:
fetch-depth: 2

- name: Install Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
check-latest: true

- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 22

- name: Install Yarn
run: |
npm install --global yarn

- name: Run stbernard deps
run: |
go tool stbernard deps

- name: Run stbernard modsync
run: |
go tool stbernard modsync

- name: Run stbernard generate
run: |
go tool stbernard generate

- name: Run stbernard license
run: |
go tool stbernard license

- name: Check for uncommitted changes
id: show
run: |
git update-index --skip-worktree cmd/ui/public/mockServiceWorker.js
go tool stbernard show -no-version

- name: Fail with helpful message
if: failure()
run: |
echo "::error::Code generation check failed!"
echo ""
echo "=========================================="
echo " CODE GENERATION CHECK FAILED"
echo "=========================================="
echo ""
echo "This PR has uncommitted generated code or dependency changes."
echo ""
echo "Please run the following command locally and commit the changes:"
echo ""
echo " just prepare-for-codereview"
echo ""
echo "Then push the updated changes to this PR."
echo ""
echo "=========================================="
exit 1
167 changes: 90 additions & 77 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,81 +1,94 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "antlr-debug",
"request": "launch",
"name": "Debug Cypher Grammar",
"input": "${file}",
"grammar": "${workspaceFolder}/packages/go/cypher/grammar/Cypher.g4",
"visualParseTree": true,
},
{
"name": "Launch Go file",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${file}",
},
{
"name": "Debug with Docker",
"type": "go",
"host": "localhost",
"mode": "remote",
"port": 3456,
"request": "attach",
"substitutePath": [
{
"type": "antlr-debug",
"request": "launch",
"name": "Debug Cypher Grammar",
"input": "${file}",
"grammar": "${workspaceFolder}/packages/go/cypher/grammar/Cypher.g4",
"visualParseTree": true
"from": "${workspaceFolder}",
"to": "/bloodhound",
},
{
"name": "Launch Go file",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${file}"
},
{
"name": "Debug with Docker",
"type": "go",
"host": "localhost",
"mode": "remote",
"port": 3456,
"request": "attach",
"substitutePath": [
{
"from": "${workspaceFolder}",
"to": "/bloodhound"
}
]
},
{
"name": "Debug API Locally",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "github.com/specterops/bloodhound/cmd/api/src",
"cwd": "${workspaceFolder}",
"args": [
"-configfile",
"${workspaceFolder}/local-harnesses/build.config.json"
]
},
{
"name": "Run Package Integration Tests",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${fileDirname}",
"buildFlags": "--tags=integration,serial_integration",
"showLog": true,
"env": {
"INTEGRATION_CONFIG_PATH": "${workspaceFolder}/local-harnesses/integration.config.json"
}
},
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/start",
"outFiles": ["${workspaceFolder}/**/*.js"]
},
{
"name": "Graphify",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "packages/go/graphify",
"cwd": "${workspaceFolder}",
"args": ["bh-graphify", "-path=${workspaceFolder}/cmd/api/src/test/fixtures/fixtures/v6/ingest", "-outpath=${workspaceFolder}/tmp/"],
"env": {
"SB_PG_CONNECTION":"user=bloodhound password=bloodhoundcommunityedition dbname=bloodhound host=localhost port=65432"
}
}
]
],
},
{
"name": "Debug API Locally",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "github.com/specterops/bloodhound/cmd/api/src",
"cwd": "${workspaceFolder}",
"args": [
"-configfile",
"${workspaceFolder}/local-harnesses/build.config.json",
],
},
{
"name": "Run Package Integration Tests",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${fileDirname}",
"buildFlags": "--tags=integration,serial_integration",
"showLog": true,
"env": {
"INTEGRATION_CONFIG_PATH": "${workspaceFolder}/local-harnesses/integration.config.json",
},
},
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/start",
"outFiles": ["${workspaceFolder}/**/*.js"],
},
{
"name": "Debug St Bernard License",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "packages/go/stbernard",
"cwd": "${workspaceFolder}",
"args": ["license"],
},
{
"name": "Graphify",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "packages/go/graphify",
"cwd": "${workspaceFolder}",
"args": [
"bh-graphify",
"-path=${workspaceFolder}/cmd/api/src/test/fixtures/fixtures/v6/ingest",
"-outpath=${workspaceFolder}/tmp/",
],
"env": {
"SB_PG_CONNECTION": "user=bloodhound password=bloodhoundcommunityedition dbname=bloodhound host=localhost port=65432",
},
},
],
}
5 changes: 4 additions & 1 deletion packages/go/stbernard/command/license/internal/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,11 @@ func Run(env environment.Environment) error {
})

// ignore directories and paths that are in the ignore list
if info.IsDir() && (slices.Contains(ignoreDir, info.Name()) || ignorePath) {
if info.IsDir() && slices.Contains(ignoreDir, info.Name()) {
return filepath.SkipDir
} else if ignorePath {
// Shortcut out without skipping directory (support specific file ignores)
return nil
}

// ignore files that are in the ignore list
Expand Down
40 changes: 28 additions & 12 deletions packages/go/stbernard/command/show/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const (
)

type command struct {
env environment.Environment
env environment.Environment
noVersion bool
}

type repository struct {
Expand Down Expand Up @@ -65,6 +66,8 @@ func (s *command) Name() string {
func (s *command) Parse(cmdIndex int) error {
cmd := flag.NewFlagSet(Name, flag.ExitOnError)

cmd.BoolVar(&s.noVersion, "no-version", false, "Disable version tag checking (for environments without git tags)")

cmd.Usage = func() {
w := flag.CommandLine.Output()
fmt.Fprintf(w, "%s\n\nUsage: %s %s [OPTIONS]\n\nOptions:\n", Usage, filepath.Base(os.Args[0]), Name)
Expand Down Expand Up @@ -93,7 +96,11 @@ func (s *command) Run() error {
for _, repo := range repos {
fmt.Printf("Repository Report For %s\n", repo.path)
fmt.Printf("Current HEAD: %s\n", repo.sha)
fmt.Printf("Detected version: %s\n", repo.version)
if s.noVersion {
fmt.Println("Detected version: (skipped)")
} else {
fmt.Printf("Detected version: %s\n", repo.version)
}
if !repo.clean {
fmt.Println("CHANGES DETECTED - please commit outstanding changes and run the command again")
return fmt.Errorf("changes detected in git repository - please commit outstanding changes and run the command again")
Expand Down Expand Up @@ -123,18 +130,27 @@ func (s *command) submodulesCheck(paths []string) ([]repository, error) {
func (s *command) repositoryCheck(cwd string) (repository, error) {
var repo repository

if sha, err := git.FetchCurrentCommitSHA(cwd, s.env); err != nil {
sha, err := git.FetchCurrentCommitSHA(cwd, s.env)
if err != nil {
return repo, fmt.Errorf("fetching current commit sha: %w", err)
} else if version, err := git.ParseLatestVersionFromTags(cwd, s.env); err != nil {
return repo, fmt.Errorf("parsing version: %w", err)
} else if clean, err := git.CheckClean(cwd, s.env); err != nil {
return repo, fmt.Errorf("checking repository clean: %w", err)
} else {
repo.path = cwd
repo.sha = sha
}

repo.path = cwd
repo.sha = sha

if !s.noVersion {
version, err := git.ParseLatestVersionFromTags(cwd, s.env)
if err != nil {
return repo, fmt.Errorf("parsing version: %w", err)
}
repo.version = version
repo.clean = clean
}

return repo, nil
clean, err := git.CheckClean(cwd, s.env)
if err != nil {
return repo, fmt.Errorf("checking repository clean: %w", err)
}
repo.clean = clean

return repo, nil
}
15 changes: 8 additions & 7 deletions packages/go/stbernard/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,20 @@ func CheckClean(cwd string, env environment.Environment) (bool, error) {

diffIndexPlan := cmdrunner.ExecutionPlan{
Command: "git",
Args: []string{"diff-index", "--quiet", "HEAD", "--"},
Args: []string{"--no-pager", "diff"},
Path: cwd,
Env: env.Slice(),
SuppressErrors: true,
}
result, err := cmdrunner.Run(context.TODO(), diffIndexPlan)
if err != nil {
// Failure was due to dirty workspace
if errors.Is(err, cmdrunner.ErrCmdExecutionFailed) && result.ReturnCode == 1 {
return false, nil
} else {
return false, fmt.Errorf("git diff-index: %w", err)
}
return false, fmt.Errorf("git diff: %w", err)
}

if len(result.StandardOutput.Bytes()) > 0 {
slog.Info("Repository is dirty")
fmt.Fprint(os.Stdout, result.StandardOutput.String())
return false, nil
}

slog.Info(fmt.Sprintf("Finished checking repository clean for %s", cwd))
Expand Down
10 changes: 10 additions & 0 deletions packages/go/stbernard/workspace/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"os"
"path/filepath"
"strings"

"github.com/specterops/bloodhound/packages/go/stbernard/cmdrunner"
"github.com/specterops/bloodhound/packages/go/stbernard/environment"
Expand Down Expand Up @@ -87,6 +89,14 @@ func FindPaths(env environment.Environment) (WorkspacePaths, error) {
return WorkspacePaths{}, fmt.Errorf("parsing yarn workspace: %w", err)
}

slog.Info(
"Detected Workspace",
slog.String("root", cwd),
slog.String("submodules", strings.Join(subPaths, "|")),
slog.String("assets", yarnWorkspaces.AssetsDir),
slog.String("yarn_workspaces", strings.Join(yarnWorkspaces.Workspaces, "|")),
)

return WorkspacePaths{
Root: cwd,
Coverage: path,
Expand Down