Skip to content

Fix source_kind contamination of AD nodes via two-step upload (#25)#26

Merged
p0dalirius merged 1 commit into
mainfrom
bugfix-two-step-upload-cross-collector
Jun 22, 2026
Merged

Fix source_kind contamination of AD nodes via two-step upload (#25)#26
p0dalirius merged 1 commit into
mainfrom
bugfix-two-step-upload-cross-collector

Conversation

@p0dalirius

Copy link
Copy Markdown
Contributor

Linked Issue

Closes #25

Root Cause

The collector built a single graph with NewOpenGraph(KindKeyCredentialBase) and added every edge to it, including the HasKeyCredential edges whose start endpoint is an existing AD principal (objectSid) not emitted by this collector. Since #15 the export emits metadata.source_kind. Per the BloodHound OpenGraph contract, a payload that sets metadata.source_kind and references existing AD/AZ nodes stamps that source kind onto the referenced nodes; deleting the source kind then deletes them. Mixing the collector's own nodes with cross-collector edges in one source-kind payload therefore put legitimate AD User/Computer nodes at risk of deletion.

Fix Description

Adopt the documented two-step upload. ParseResults now takes a second graph, ogCrossCollector, created with an empty source kind. The collector's own nodes and HasKeyMaterial edges stay in the source-kind graph (og); the cross-collector HasKeyCredential edges go into ogCrossCollector. main writes two files: the main <output>.json (with metadata.source_kind) and <output>_cross_collector.json (no metadata, because an empty source kind suppresses the metadata block). Export was factored into a writeGraph helper and the cross filename into crossCollectorOutputFile. The README documents the two files and the required upload order.

How Verified

  • Runtime: a temporary test built both graphs and serialized them. The main graph contains "metadata": {"source_kind": "KeyCredentialBase"}; the cross-collector graph (NewOpenGraph(""), ExportJSON(true)) contains no metadata/source_kind block. Confirmed against gopengraph/OpenGraph.go:577 where the metadata block is gated on g.sourceKind != "".
  • Tests: TestCrossCollectorOutputFile covers the filename derivation; go test ./... passes.
  • go build ./..., go vet ./..., and gofmt -l (no output) pass.

Test Coverage

Added: main_test.go / TestCrossCollectorOutputFile covers the _cross_collector filename derivation across extension, no-extension, nested-path, and non-.json cases. The graph-routing change itself has no fixture harness (requires live LDAP) and was verified at runtime as described above.

Scope of Change

  • Files changed: parse.go, main.go, README.md, main_test.go
  • Submodule pointer updated: no
  • Behavioral changes outside the bug fix: the collector now writes two output files instead of one (the second is the cross-collector edge payload). This is the substance of the fix; documented in the README.

Risk and Rollout

Local to the collector. Operators must now upload two files (main first, then cross-collector). Existing single-file workflows must add the second upload to keep the HasKeyCredential edges. Safe to merge.

@p0dalirius
p0dalirius merged commit b0e8bd1 into main Jun 22, 2026
8 checks passed
@p0dalirius
p0dalirius deleted the bugfix-two-step-upload-cross-collector branch June 22, 2026 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

source_kind contaminates referenced AD principals; deleting it deletes AD nodes

1 participant