Skip to content

Commit

Permalink
Merge branch 'main' of github.com:SpecterOps/BloodHound into pg-migrate
Browse files Browse the repository at this point in the history
  • Loading branch information
maffkipp committed May 16, 2024
2 parents 3ffa5b9 + d9e67f9 commit c0226b8
Show file tree
Hide file tree
Showing 47 changed files with 1,152 additions and 722 deletions.
34 changes: 23 additions & 11 deletions .github/workflows/jira-issue-transfer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ name: Jira Issue Transfer

on:
issues:
types:
- opened
types:
- labeled
jobs:
build:
runs-on: self-hosted
Expand All @@ -29,13 +29,25 @@ jobs:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
- name: Jira Create
uses: atlassian/gajira-create@v3
with:
project: BP
issuetype: 'Bug Report'
summary: ${{ github.event.issue.title }}
description: "Github Issue Link: ${{ github.event.issue.url}} \r\n ${{ github.event.issue.body }}"
fields: '{"labels":["GitHubReport"]}'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
if: github.event.label.name == 'ticketed' && !contains(github.event.issue.labels.*.name, 'ticketed')
if: contains(github.event.issue.labels.*.name, 'bug')
- name: Jira Create
uses: atlassian/gajira-create@v3
with:
project: BP
issuetype: 'Bug Report'
summary: ${{ github.event.issue.title }}
description: "Github Issue Link: ${{ github.event.issue.html_url}} \r\n ${{ github.event.issue.body }}"
fields: '{"labels":["GitHubReport"]}'

if: contains(github.event.issue.labels.*.name, 'enhancement')
- name: Jira Create
uses: atlassian/gajira-create@v3
with:
project: BP
issuetype: 'Product Feature'
summary: ${{ github.event.issue.title }}
description: "Github Issue Link: ${{ github.event.issue.html_url}} \r\n ${{ github.event.issue.body }}"
fields: '{"labels":["GitHubReport"]}'

55 changes: 55 additions & 0 deletions .github/workflows/pr-jira-issue-transfer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 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
name: PR-Ticket-OoOrg

on:
pull_request:
types: [opened, synchronize]

jobs:
build:
runs-on: self-hosted
steps:
- name: Check if PR is from within the organization
id: check_org
run:
PR_URL=$(jq -r .pull_request.url "$GITHUB_EVENT_PATH")
PR_NUMBER=$(jq -r .pull_request.number "$GITHUB_EVENT_PATH")
TEAM_SLUG="bloodhound-engineering" # Replace with your team's slug
GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}"
PR_ORG=$(jq -r .pull_request.head.repo.organization.login "$GITHUB_EVENT_PATH")
PR_USER=$(jq -r .pull_request.user.login "$GITHUB_EVENT_PATH")
TEAM_MEMBERSHIP=$(curl -s -X GET -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/orgs/$PR_ORG/teams/$TEAM_SLUG/memberships/$PR_USER")

env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

if [ -z "$TEAM_MEMBERSHIP" ]; then
- name: Login
uses: atlassian/gajira-login@v3
- name: Jira Create
uses: atlassian/gajira-create@v3
with:
project: BP
issuetype: 'Product Feature'
summary: ${{ github.event.pulls.title }}
description: "Github Pull Request Link: ${{ github.event.pulls.html_url}} \r\n ${{ github.event.pulls.body }}"
fields: '{"labels":["GitHubReport"]}'

15 changes: 8 additions & 7 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"
ad2 "github.com/specterops/bloodhound/analysis/ad"
"github.com/specterops/bloodhound/analysis/impact"
Expand Down Expand Up @@ -2928,42 +2929,42 @@ func TestExtendedByPolicyBinding(t *testing.T) {
operation.Done()

db.ReadTransaction(context.Background(), func(tx graph.Transaction) error {
if edge, err := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.IssuancePolicy0.ID, harness.ExtendedByPolicyHarness.CertTemplate1.ID, ad.ExtendedByPolicy); err != nil {
if edge, err := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.CertTemplate1.ID, harness.ExtendedByPolicyHarness.IssuancePolicy0.ID, ad.ExtendedByPolicy); err != nil {
t.Fatalf("error fetching ExtendedByPolicy edge (1) in integration test; %v", err)
} else {
require.NotNil(t, edge)
}

if edge, err := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.IssuancePolicy1.ID, harness.ExtendedByPolicyHarness.CertTemplate1.ID, ad.ExtendedByPolicy); err != nil {
if edge, err := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.CertTemplate1.ID, harness.ExtendedByPolicyHarness.IssuancePolicy1.ID, ad.ExtendedByPolicy); err != nil {
t.Fatalf("error fetching ExtendedByPolicy edge (2) in integration test; %v", err)
} else {
require.NotNil(t, edge)
}

if edge, err := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.IssuancePolicy0.ID, harness.ExtendedByPolicyHarness.CertTemplate2.ID, ad.ExtendedByPolicy); err != nil {
if edge, err := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.CertTemplate2.ID, harness.ExtendedByPolicyHarness.IssuancePolicy0.ID, ad.ExtendedByPolicy); err != nil {
t.Fatalf("error fetching ExtendedByPolicy edge (3) in integration test; %v", err)
} else {
require.NotNil(t, edge)
}

if edge, err := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.IssuancePolicy2.ID, harness.ExtendedByPolicyHarness.CertTemplate2.ID, ad.ExtendedByPolicy); err != nil {
if edge, err := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.CertTemplate2.ID, harness.ExtendedByPolicyHarness.IssuancePolicy2.ID, ad.ExtendedByPolicy); err != nil {
t.Fatalf("error fetching ExtendedByPolicy edge (4) in integration test; %v", err)
} else {
require.NotNil(t, edge)
}

if edge, err := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.IssuancePolicy3.ID, harness.ExtendedByPolicyHarness.CertTemplate3.ID, ad.ExtendedByPolicy); err != nil {
if edge, err := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.CertTemplate3.ID, harness.ExtendedByPolicyHarness.IssuancePolicy3.ID, ad.ExtendedByPolicy); err != nil {
t.Fatalf("error fetching ExtendedByPolicy edge (5) in integration test; %v", err)
} else {
require.NotNil(t, edge)
}

// CertificatePolicy doesn't match CertTemplateOID
edge, _ := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.IssuancePolicy1.ID, harness.ExtendedByPolicyHarness.CertTemplate2.ID, ad.ExtendedByPolicy)
edge, _ := analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.CertTemplate2.ID, harness.ExtendedByPolicyHarness.IssuancePolicy1.ID, ad.ExtendedByPolicy)
require.Nil(t, edge, "ExtendedByPolicy edge exists between IssuancePolicy1 and CertTemplate2 where it shouldn't")

// Different domains, no edge
edge, _ = analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.IssuancePolicy4.ID, harness.ExtendedByPolicyHarness.CertTemplate4.ID, ad.ExtendedByPolicy)
edge, _ = analysis.FetchEdgeByStartAndEnd(testContext.Context(), db, harness.ExtendedByPolicyHarness.CertTemplate4.ID, harness.ExtendedByPolicyHarness.IssuancePolicy4.ID, ad.ExtendedByPolicy)
require.Nil(t, edge, "ExtendedByPolicy edge bridges domains where it shouldn't")

return nil
Expand Down
5 changes: 5 additions & 0 deletions cmd/api/src/api/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ import (
"github.com/unrolled/secure"
)

const (
// Default timeout for any request is thirty seconds
defaultTimeout = 30 * time.Second
)

// Wrapper is an iterator for middleware function application that wraps around a http.Handler.
type Wrapper struct {
middleware []mux.MiddlewareFunc
Expand Down
13 changes: 13 additions & 0 deletions cmd/api/src/api/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,16 @@ func SignRequestAtTime(hasher func() hash.Hash, id string, token string, datetim
func SignRequest(tokenID, token string, request *http.Request) error {
return SignRequestAtTime(sha256.New, tokenID, token, time.Now(), request)
}

type readerDelegatedCloser struct {
source io.Reader
closer io.Closer
}

func (s readerDelegatedCloser) Read(p []byte) (n int, err error) {
return s.source.Read(p)
}

func (s readerDelegatedCloser) Close() error {
return s.closer.Close()
}
2 changes: 2 additions & 0 deletions cmd/api/src/api/v2/cypherquery.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ func (s Resources) CypherQuery(response http.ResponseWriter, request *http.Reque
} else {
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusInternalServerError, err.Error(), request), response)
}
} else if !preparedQuery.HasMutation && len(graphResponse.Nodes)+len(graphResponse.Edges) == 0 {
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusNotFound, "resource not found", request), response)
} else {
api.WriteBasicResponse(request.Context(), graphResponse, http.StatusOK, response)
}
Expand Down
17 changes: 8 additions & 9 deletions cmd/api/src/api/v2/cypherquery_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,12 @@ package v2_test

import (
"bytes"
"fmt"
"testing"

"github.com/specterops/bloodhound/cypher/backend/cypher"
"github.com/specterops/bloodhound/src/auth"
"github.com/specterops/bloodhound/src/model"
"github.com/specterops/bloodhound/src/utils/test"
"net/http"
"testing"

"github.com/specterops/bloodhound/cypher/frontend"
"github.com/specterops/bloodhound/graphschema/common"
Expand Down Expand Up @@ -144,7 +143,7 @@ func Test_CypherSearch_WithCypherMutationsEnabled(t *testing.T) {
assert.True(ok)

var (
query = "match (w) where w.name = 'vldmrt' remove w.name return w"
query = "match (w: Wizard) where w.name = 'vldmrt' remove w.name return w"
strippedQuery = &bytes.Buffer{}
)
parsedQuery, err := frontend.ParseCypher(parseCtx, query)
Expand All @@ -169,7 +168,7 @@ func Test_CypherSearch_WithCypherMutationsEnabled(t *testing.T) {
assert.True(ok)

var (
query = "match (w) where w.name = 'harryp' delete w"
query = "match (w: Wizard) where w.name = 'harryp' delete w"
strippedQuery = &bytes.Buffer{}
)
parsedQuery, err := frontend.ParseCypher(parseCtx, query)
Expand All @@ -192,12 +191,12 @@ func Test_CypherSearch_WithCypherMutationsEnabled(t *testing.T) {
assert.True(found)
}),

lab.TestCase("adds failed mutation attempts to audit log", func(assert *require.Assertions, harness *lab.Harness) {
lab.TestCase("adds mutation attempts to audit log", func(assert *require.Assertions, harness *lab.Harness) {
apiClient, ok := lab.Unpack(harness, adminApiClientFixture)
assert.True(ok)

var (
query = "match (w) set w.wizard = true return w.wizard"
query = "match (w: Wizard) set w.wizard = true return w.wizard"
strippedQuery = &bytes.Buffer{}
)

Expand All @@ -207,12 +206,12 @@ func Test_CypherSearch_WithCypherMutationsEnabled(t *testing.T) {
require.Nil(t, err)

_, err = apiClient.CypherQuery(v2.CypherQueryPayload{Query: query})
assert.ErrorContains(err, fmt.Sprintf("%d", http.StatusInternalServerError))
require.Nil(t, err)

auditLogs, err := apiClient.GetLatestAuditLogs()
assert.Nil(err)

err = test.AssertAuditLogs(auditLogs.Logs, model.AuditLogActionMutateGraph, model.AuditLogStatusFailure, model.AuditData{"query": strippedQuery.String()})
err = test.AssertAuditLogs(auditLogs.Logs, model.AuditLogActionMutateGraph, model.AuditLogStatusSuccess, model.AuditData{"query": strippedQuery.String()})
assert.Nil(err)
}),
)
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 @@ -131,7 +131,7 @@ func (s Resources) ProcessFileUpload(response http.ResponseWriter, request *http
}

if !IsValidContentTypeForUpload(request.Header) {
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, "Content type must be application/json or application/zip", request), response)
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, fmt.Sprintf("Content type must be application/json or application/zip"), request), response)
} else if fileUploadJobID, err := strconv.Atoi(fileUploadJobIdString); err != nil {
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, api.ErrorResponseDetailsIDMalformed, request), response)
} else if fileUploadJob, err := fileupload.GetFileUploadJobByID(request.Context(), s.DB, int64(fileUploadJobID)); err != nil {
Expand Down
Loading

0 comments on commit c0226b8

Please sign in to comment.