Skip to content

Commit

Permalink
feat: static analysis (#327)
Browse files Browse the repository at this point in the history
* fix: static analyzer highlighted issues

* feat: expermintal analysis command added to support Go and JS analysis

* feat: reorg code and add exit code handling logic

* feat: found eslint rules to fail these builds

* chore: license check run

* temp: debug output of eslint

* revert: debugging change

* feat: use --quiet to reduce junk JSON

* chore: split code to be a little easier to maintain later

* fix: breakage when err == nil

* fix: tune linter rules to provide one low and one high per language

* chore: readjust our warning/errors for JS

* fix: make testing graph db less resource intensive for now reason

* fix: change existing errors to warnings for now

* feat: add preanalysis steps to stbernard

* feat: moved stbernard to the BloodHound logger
fix: various log messages given better filtering

* chore: make clean builds a bit more robust

* fix: license check patched to allow for different years
fix: license check will now start stamping license with year 2024

* fix: copyright dates
  • Loading branch information
superlinkx authored Jan 23, 2024
1 parent 37162fc commit 1891327
Show file tree
Hide file tree
Showing 35 changed files with 622 additions and 69 deletions.
15 changes: 13 additions & 2 deletions .golangci.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"unused"
],
"enable": [
"gosimple"
"gosimple",
"stylecheck"
]
},
"issues": {
Expand Down Expand Up @@ -49,11 +50,21 @@
"serial_integration"
]
},
"severity": {
"default-severity": "error",
"rules": [
{
"text": "(ST\\d{4}|S\\d{4})",
"severity": "warning"
}
]
},
"linters-settings": {
"stylecheck": {
"checks": [
"all",
"-ST1000"
"-ST1000",
"-ST1003"
]
}
}
Expand Down
10 changes: 8 additions & 2 deletions cmd/api/src/analysis/ad/adcs_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package ad_test

import (
"context"

"github.com/specterops/bloodhound/analysis"
"github.com/specterops/bloodhound/graphschema"

Expand Down Expand Up @@ -55,6 +56,7 @@ func TestADCSESC1(t *testing.T) {
certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate)
require.Nil(t, err)
domains, err := ad2.FetchNodesByKind(context.Background(), db, ad.Domain)
require.Nil(t, err)

cache := ad2.NewADCSCache()
cache.BuildCache(context.Background(), db, enterpriseCertAuthorities, certTemplates)
Expand Down Expand Up @@ -427,14 +429,16 @@ func TestEnrollOnBehalfOf(t *testing.T) {
return nil
}, func(harness integration.HarnessDetails, db graph.Database) {
certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate)
v1Templates := make([]*graph.Node, 0)
// TODO: v1Templates are never used in any assertions and should either have assertions added or be removed from the test entirely
//v1Templates := make([]*graph.Node, 0)
v2Templates := make([]*graph.Node, 0)

for _, template := range certTemplates {
if version, err := template.Properties.Get(ad.SchemaVersion.String()).Float64(); err != nil {
continue
} else if version == 1 {
v1Templates = append(v1Templates, template)
continue
//v1Templates = append(v1Templates, template)
} else if version >= 2 {
v2Templates = append(v2Templates, template)
}
Expand Down Expand Up @@ -473,6 +477,7 @@ func TestADCSESC3(t *testing.T) {
certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate)
require.Nil(t, err)
domains, err := ad2.FetchNodesByKind(context.Background(), db, ad.Domain)
require.Nil(t, err)

cache := ad2.NewADCSCache()
cache.BuildCache(context.Background(), db, enterpriseCertAuthorities, certTemplates)
Expand Down Expand Up @@ -529,6 +534,7 @@ func TestADCSESC3(t *testing.T) {
certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate)
require.Nil(t, err)
domains, err := ad2.FetchNodesByKind(context.Background(), db, ad.Domain)
require.Nil(t, err)

cache := ad2.NewADCSCache()
cache.BuildCache(context.Background(), db, enterpriseCertAuthorities, certTemplates)
Expand Down
2 changes: 1 addition & 1 deletion cmd/api/src/api/middleware/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func AuthMiddleware(authenticator api.Authenticator) mux.MiddlewareFunc {
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, "Token ID is malformed.", request), response)
return
} else if userAuth, responseCode, err := authenticator.ValidateRequestSignature(tokenID, request, time.Now()); err != nil {
msg := fmt.Errorf("Unable to validate request signature for client: %w.", err).Error()
msg := fmt.Errorf("unable to validate request signature for client: %w", err).Error()
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(responseCode, msg, request), response)
return
} else {
Expand Down
3 changes: 2 additions & 1 deletion cmd/api/src/api/v2/apiclient/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ package apiclient

import (
"fmt"
"net/http"

"github.com/specterops/bloodhound/src/api"
"github.com/specterops/bloodhound/src/model/appcfg"
"net/http"
)

func (s Client) GetFeatureFlags() ([]appcfg.FeatureFlag, error) {
Expand Down
2 changes: 1 addition & 1 deletion cmd/api/src/api/v2/file_uploads.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (s Resources) ProcessFileUpload(response http.ResponseWriter, request *http
} else if fileUploadJob, err := fileupload.GetFileUploadJobByID(s.DB, int64(fileUploadJobID)); err != nil {
api.HandleDatabaseError(request, response, err)
} else if fileName, err := fileupload.SaveIngestFile(s.Config.TempDirectory(), request.Body); err != nil {
if errors.Is(err, fileupload.InvalidIngestFileType) {
if errors.Is(err, fileupload.ErrInvalidIngestFileType) {
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, fmt.Sprintf("Error saving ingest file: %v", err), request), response)
} else {
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusInternalServerError, fmt.Sprintf("Error saving ingest file: %v", err), request), response)
Expand Down
2 changes: 1 addition & 1 deletion cmd/api/src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func SetValuesFromEnv(varPrefix string, target any, env []string) error {
if formattedPrefix := formatEnvironmentVariablePrefix(varPrefix); strings.HasPrefix(key, formattedPrefix) {
cfgKeyPath := strings.TrimPrefix(key, formattedPrefix)

if err := SetValue(target, cfgKeyPath, valueStr); errors.Is(err, InvalidConfigurationPathError) {
if err := SetValue(target, cfgKeyPath, valueStr); errors.Is(err, ErrInvalidConfigurationPath) {
log.Warnf("%s", err)
} else if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions cmd/api/src/config/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
)

var structTagRegex = regexp.MustCompile(`(\w+):"([^"]+)"`)
var InvalidConfigurationPathError = errors.New("Unable to find a configuration element by path")
var ErrInvalidConfigurationPath = errors.New("unable to find a configuration element by path")

// taggedField represents a struct field by its index and a parsed representation of any tags associated with the
// struct field.
Expand Down Expand Up @@ -293,7 +293,7 @@ func SetValue(target any, path, value string) error {
}

if !found {
return fmt.Errorf("%w: %s", InvalidConfigurationPathError, path)
return fmt.Errorf("%w: %s", ErrInvalidConfigurationPath, path)
}
}

Expand Down
14 changes: 7 additions & 7 deletions cmd/api/src/database/auth_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// Copyright 2023 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

//go:build integration
Expand Down Expand Up @@ -240,9 +240,9 @@ func TestDatabase_CreateGetUser(t *testing.T) {

func TestDatabase_CreateGetDeleteAuthToken(t *testing.T) {
var (
dbInst, user = initAndCreateUser(t)
expectedName string = "test"
token = model.AuthToken{
dbInst, user = initAndCreateUser(t)
expectedName = "test"
token = model.AuthToken{
UserID: database.NullUUID(user.ID),
Key: "key",
HmacMethod: "fake",
Expand Down
4 changes: 2 additions & 2 deletions cmd/api/src/services/fileupload/file_upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (

const jobActivityTimeout = time.Minute * 20

var InvalidIngestFileType = errors.New("file is not a valid content type")
var ErrInvalidIngestFileType = errors.New("file is not a valid content type")

type FileUploadData interface {
CreateFileUploadJob(job model.FileUploadJob) (model.FileUploadJob, error)
Expand Down Expand Up @@ -99,7 +99,7 @@ func SaveIngestFile(location string, fileData io.ReadCloser) (string, error) {
} else if n, err := tempFile.Read(typeBuf); err != nil {
return "", fmt.Errorf("error reading head of ingest file: %w", err)
} else if fileType := http.DetectContentType(typeBuf[:n]); !strings.Contains(fileType, "text/plain") {
return "", InvalidIngestFileType
return "", ErrInvalidIngestFileType
} else {
return tempFile.Name(), nil
}
Expand Down
1 change: 1 addition & 0 deletions cmd/ui/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ module.exports = {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-inferrable-types': 'off',
'@typescript-eslint/no-empty-function': 'off',
'prefer-const': 'warn',
},
};
4 changes: 4 additions & 0 deletions cmd/ui/src/views/Home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ const Home: React.FC = () => {
};

export default Home;

// TODO: Remove before merge to fix linter messages
let arr = [].reduce(function(prev: never, curr: never, idx: number, arr: never[]) {})

5 changes: 2 additions & 3 deletions docker-compose.testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,10 @@ services:

testgraph:
restart: unless-stopped
build:
context: tools/docker-compose
dockerfile: neo4j.Dockerfile
image: neo4j:4.4.0
environment:
- NEO4J_AUTH=neo4j/bloodhoundcommunityedition
- NEO4J_dbms_security_auth__enabled:false
ports:
- 127.0.0.1:37687:7687
- 127.0.0.1:37474:7474
Expand Down
5 changes: 5 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ bh-clear-volumes target='dev' *ARGS='':

# build BH target cleanly (default profile dev with --no-cache flag)
bh-clean-docker-build target='dev' *ARGS='':
# Ensure the target is down first
@docker compose --profile {{target}} -f docker-compose.dev.yml down
# Pull any updated images
@docker compose --profile {{target}} -f docker-compose.dev.yml pull
# Build without cache
@docker compose --profile {{target}} -f docker-compose.dev.yml build --no-cache {{ARGS}}

# build local BHCE container image (ex: just build-bhce-container <linux/arm64|linux/amd64> edge v5.0.0)
Expand Down
7 changes: 6 additions & 1 deletion license_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import os
import pathlib
import re

from typing import List

Expand Down Expand Up @@ -239,7 +240,7 @@
"""

# Apache License 2.0 header copy
LICENSE_HEADER = """Copyright 2023 Specter Ops, Inc.
LICENSE_HEADER = """Copyright 2024 Specter Ops, Inc.
Licensed under the Apache License, Version 2.0
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -389,6 +390,10 @@ def content_has_header(path: str, content_lines: List[str], header: str) -> bool
if header_lineno >= len(header_lines):
return True

elif re.search('Copyright \d{4} Specter Ops, Inc.', line.strip()):
matching_header=True
header_lineno += 1

elif matching_header:
print(f"WARNING: Path {path} contains damaged license information.")
return True
Expand Down
12 changes: 6 additions & 6 deletions packages/go/conftool/config/config.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// Copyright 2023 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

package config
Expand All @@ -29,15 +29,15 @@ type Argon2Configuration struct {
NumThreads uint8 `json:"num_threads"`
}

func GenerateArgonSettings(tuneMillis time.Duration, skipArgon2 bool) (Argon2Configuration, error) {
func GenerateArgonSettings(tuneDuration time.Duration, skipArgon2 bool) (Argon2Configuration, error) {
var (
digester crypto.Argon2
err error
)

if skipArgon2 {
return Argon2Configuration{}, nil
} else if digester, err = crypto.Tune(time.Millisecond * tuneMillis); err != nil {
} else if digester, err = crypto.Tune(time.Millisecond * tuneDuration); err != nil {
return Argon2Configuration{}, fmt.Errorf("failed tuning argon2: %w", err)
} else {
return Argon2Configuration{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Specter Ops, Inc.
// Copyright 2024 Specter Ops, Inc.
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Specter Ops, Inc.
// Copyright 2024 Specter Ops, Inc.
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
Expand All @@ -17,13 +17,14 @@
package pgtransition_test

import (
"testing"

"github.com/specterops/bloodhound/cypher/backend/pgsql/pgtransition"
"github.com/specterops/bloodhound/dawgs/graph"
"github.com/specterops/bloodhound/dawgs/query"
"github.com/specterops/bloodhound/graphschema/ad"
"github.com/specterops/bloodhound/src/test"
"github.com/stretchr/testify/require"
"testing"
)

type kindMapper struct{}
Expand Down
2 changes: 1 addition & 1 deletion packages/go/cypher/model/functions.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Specter Ops, Inc.
// Copyright 2024 Specter Ops, Inc.
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion packages/go/cypher/model/pg/extension.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Specter Ops, Inc.
// Copyright 2024 Specter Ops, Inc.
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
Expand Down
5 changes: 3 additions & 2 deletions packages/go/cypher/model/pg/model.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Specter Ops, Inc.
// Copyright 2024 Specter Ops, Inc.
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
Expand All @@ -19,11 +19,12 @@ package pg
import (
"errors"
"fmt"
"time"

"github.com/jackc/pgtype"
"github.com/specterops/bloodhound/cypher/model"
pgModel "github.com/specterops/bloodhound/dawgs/drivers/pg/model"
"github.com/specterops/bloodhound/dawgs/graph"
"time"
)

var (
Expand Down
Loading

0 comments on commit 1891327

Please sign in to comment.