-
Notifications
You must be signed in to change notification settings - Fork 267
Add local privilege collection #1999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅ |
WalkthroughThis PR extends the system to collect and process multiple local user privileges on machines, expanding beyond the previously supported SeRemoteInteractiveLogonRight. It adds 10 new privilege types across data models, parsing logic, schema definitions, and UI components, enabling detection of local privilege escalation vectors through user rights. Changes
Sequence DiagramsequenceDiagram
participant OU as OU with GPO Data
participant Parse as ParseGPOUserRights()
participant Map as Privilege→Kind Mapping
participant Out as Nodes & Relationships
OU->>Parse: GPOUserRights (members per privilege)
Parse->>Parse: Filter privileges with members
Parse->>Map: Map privilege names to graph kinds
Map->>Parse: Kind mapping (e.g., DebugPrivilege)
Parse->>Out: Create computer node (marked HasURA)
Parse->>Out: Emit ACL-style relationships<br/>(member→computer with kind)
Out-->>Out: Ingestible data
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes The changes are systematic additions across multiple layers with coherent patterns (new types, new schema kinds, new parsing logic, new UI elements), but heterogeneous in scope. The parsing logic in convertors.go and the ParseGPOUserRights function introduce moderate logic density. The repetitive nature of 11 similar privilege kind declarations reduces review complexity compared to purely diverse changes. Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
I have read the CLA Document and I hereby sign the CLA |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (3)
packages/go/ein/ad.go (2)
856-863: Set HasURA only when at least one edge is emitted for the computerCurrently a Computer node with HasURA=true is created for every AffectedComputer, even when no members exist for any mapped right. Gate HasURA on actual edge emission to avoid noisy nodes.
Apply:
func ParseGPOUserRights(privileges GPOUserRights) ParsedUserRightsData { parsedData := ParsedUserRightsData{} @@ - for _, computer := range privileges.AffectedComputers { - computerNode := IngestibleNode{ - ObjectID: computer.ObjectIdentifier, - PropertyMap: map[string]any{ad.HasURA.String(): true}, - Labels: []graph.Kind{ad.Computer}, - } - parsedData.Nodes = append(parsedData.Nodes, computerNode) - - for _, privilege := range privilegesToProcess { - edgeType := privilegeMap[privilege.Name] - - for _, member := range privilege.Members { - parsedData.Relationships = append(parsedData.Relationships, NewIngestibleRelationship( + for _, computer := range privileges.AffectedComputers { + hasAny := false + for _, privilege := range privilegesToProcess { + edgeType := privilegeMap[privilege.Name] + for _, member := range privilege.Members { + parsedData.Relationships = append(parsedData.Relationships, NewIngestibleRelationship( IngestibleEndpoint{ Value: member.ObjectIdentifier, Kind: member.Kind(), }, IngestibleEndpoint{ Value: computer.ObjectIdentifier, Kind: ad.Computer, }, IngestibleRel{ RelProps: map[string]any{ ad.IsACL.String(): false, }, RelType: edgeType, }, )) + hasAny = true } } + if hasAny { + parsedData.Nodes = append(parsedData.Nodes, IngestibleNode{ + ObjectID: computer.ObjectIdentifier, + PropertyMap: map[string]any{ad.HasURA.String(): true}, + Labels: []graph.Kind{ad.Computer}, + }) + } }Also applies to: 864-885
844-855: Optional: determinism for testsConsider sorting privilegesToProcess (by Name) and each privilege.Members (by ObjectIdentifier) to make outputs deterministic, which stabilizes tests and diffs.
cmd/api/src/services/graphify/convertors.go (1)
110-123: Set HasURA once after the loop; remove per-case duplicationReduce repetition and minor map churn by toggling a local hasURA flag in cases and assigning the property once after processing all user rights.
Apply:
@@ - for _, userRight := range computer.UserRights { + hasURA := false + for _, userRight := range computer.UserRights { @@ - case ein.UserRightInteractiveLogon: - converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.InteractiveLogonRight)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + case ein.UserRightInteractiveLogon: + converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.InteractiveLogonRight)...) + hasURA = true @@ - case ein.UserRightRemoteInteractiveLogon: - converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.RemoteInteractiveLogonRight)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + case ein.UserRightRemoteInteractiveLogon: + converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.RemoteInteractiveLogonRight)...) + hasURA = true @@ - case ein.UserRightAssignPrimaryTokenPrivilege: + case ein.UserRightAssignPrimaryTokenPrivilege: converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.AssignPrimaryTokenPrivilege)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + hasURA = true @@ - case ein.UserRightBackupPrivilege: + case ein.UserRightBackupPrivilege: converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.BackupPrivilege)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + hasURA = true @@ - case ein.UserRightCreateTokenPrivilege: + case ein.UserRightCreateTokenPrivilege: converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.CreateTokenPrivilege)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + hasURA = true @@ - case ein.UserRightDebugPrivilege: + case ein.UserRightDebugPrivilege: converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.DebugPrivilege)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + hasURA = true @@ - case ein.UserRightImpersonatePrivilege: + case ein.UserRightImpersonatePrivilege: converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.ImpersonatePrivilege)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + hasURA = true @@ - case ein.UserRightLoadDriverPrivilege: + case ein.UserRightLoadDriverPrivilege: converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.LoadDriverPrivilege)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + hasURA = true @@ - case ein.UserRightManageVolumePrivilege: + case ein.UserRightManageVolumePrivilege: converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.ManageVolumePrivilege)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + hasURA = true @@ - case ein.UserRightRestorePrivilege: + case ein.UserRightRestorePrivilege: converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.RestorePrivilege)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + hasURA = true @@ - case ein.UserRightTakeOwnershipPrivilege: + case ein.UserRightTakeOwnershipPrivilege: converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.TakeOwnershipPrivilege)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + hasURA = true @@ - case ein.UserRightTcbPrivilege: + case ein.UserRightTcbPrivilege: converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.TcbPrivilege)...) - baseNodeProp.PropertyMap[ad.HasURA.String()] = true + hasURA = true } } + if hasURA { + baseNodeProp.PropertyMap[ad.HasURA.String()] = true + }Also applies to: 124-137, 138-152
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
cmd/api/src/services/graphify/convertors.go(2 hunks)packages/cue/bh/ad/ad.cue(2 hunks)packages/go/ein/ad.go(1 hunks)packages/go/ein/incoming_models.go(2 hunks)packages/go/ein/models.go(1 hunks)packages/go/graphschema/ad/ad.go(2 hunks)packages/javascript/bh-shared-ui/src/commonSearchesAGT.ts(1 hunks)packages/javascript/bh-shared-ui/src/graphSchema.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
packages/go/ein/ad.go (3)
packages/go/ein/incoming_models.go (15)
GPOUserRights(370-373)UserRightInteractiveLogon(31-31)UserRightRemoteInteractiveLogon(32-32)UserRightAssignPrimaryTokenPrivilege(33-33)UserRightBackupPrivilege(34-34)UserRightCreateTokenPrivilege(35-35)UserRightDebugPrivilege(36-36)UserRightImpersonatePrivilege(37-37)UserRightLoadDriverPrivilege(38-38)UserRightManageVolumePrivilege(39-39)UserRightRestorePrivilege(40-40)UserRightTakeOwnershipPrivilege(41-41)UserRightTcbPrivilege(42-42)Privilege(375-378)Computer(340-360)packages/go/ein/models.go (5)
ParsedUserRightsData(92-95)IngestibleNode(77-81)NewIngestibleRelationship(22-33)IngestibleEndpoint(45-49)IngestibleRel(51-54)packages/go/graphschema/ad/ad.go (17)
InteractiveLogonRight(82-82)RemoteInteractiveLogonRight(83-83)AssignPrimaryTokenPrivilege(84-84)BackupPrivilege(85-85)CreateTokenPrivilege(86-86)DebugPrivilege(87-87)ImpersonatePrivilege(88-88)LoadDriverPrivilege(89-89)ManageVolumePrivilege(90-90)RestorePrivilege(91-91)TakeOwnershipPrivilege(92-92)TcbPrivilege(93-93)HasURA(185-185)Computer(30-30)Nodes(1157-1159)Relationships(1160-1162)IsACL(169-169)
packages/go/ein/models.go (2)
packages/go/graphschema/ad/ad.go (2)
Relationships(1160-1162)Nodes(1157-1159)packages/go/graphschema/common/common.go (2)
Relationships(36-38)Nodes(33-35)
cmd/api/src/services/graphify/convertors.go (3)
packages/go/ein/incoming_models.go (14)
Privilege(375-378)UserRightInteractiveLogon(31-31)UserRightRemoteInteractiveLogon(32-32)UserRightAssignPrimaryTokenPrivilege(33-33)UserRightBackupPrivilege(34-34)UserRightCreateTokenPrivilege(35-35)UserRightDebugPrivilege(36-36)UserRightImpersonatePrivilege(37-37)UserRightLoadDriverPrivilege(38-38)UserRightManageVolumePrivilege(39-39)UserRightRestorePrivilege(40-40)UserRightTakeOwnershipPrivilege(41-41)UserRightTcbPrivilege(42-42)GPOUserRights(370-373)packages/go/ein/ad.go (2)
ParseUserRightData(1292-1313)ParseGPOUserRights(826-889)packages/go/graphschema/ad/ad.go (15)
InteractiveLogonRight(82-82)HasURA(185-185)RemoteInteractiveLogonRight(83-83)AssignPrimaryTokenPrivilege(84-84)BackupPrivilege(85-85)CreateTokenPrivilege(86-86)DebugPrivilege(87-87)ImpersonatePrivilege(88-88)LoadDriverPrivilege(89-89)ManageVolumePrivilege(90-90)RestorePrivilege(91-91)TakeOwnershipPrivilege(92-92)TcbPrivilege(93-93)Relationships(1160-1162)Nodes(1157-1159)
🔇 Additional comments (6)
packages/go/ein/models.go (1)
92-95: Struct addition looks correctParsedUserRightsData mirrors ParsedLocalGroupData and aligns with downstream usage. No issues.
packages/go/ein/incoming_models.go (2)
25-43: Constants set is consistent with Windows user-right namesThe constants map cleanly to the Se* right names used in collectors and parsers. Looks good.
370-379: Struct additions LGTMGPOUserRights and Privilege shapes are minimal and sufficient. Adding OU.GPOUserRights integrates with convertors.
Also applies to: 385-386
cmd/api/src/services/graphify/convertors.go (1)
229-232: OU GPO user-rights ingestion wired correctlyAppending ParseGPOUserRights results into converted data for OUs looks good.
Please confirm the collector populates OU.GPOUserRights for your test data so this path is exercised.
packages/javascript/bh-shared-ui/src/commonSearchesAGT.ts (1)
567-592: Mirror these queries in commonSearchesAGI.tsHeader notes there are two files. Ensure the same “Local Privileges” section is added to commonSearchesAGI.ts to keep AGT/AGI parity.
packages/go/graphschema/ad/ad.go (1)
82-93: New privilege kinds added — ensure they’re actually exposed as relationships.Constants are fine, but most are not present in Relationships(), so they won’t surface in the API until wired via CUE RelationshipKinds and codegen. Confirm intended set and regenerate Go.
| InteractiveLogonRight: types.#Kind & { | ||
| symbol: "InteractiveLogonRight" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| RemoteInteractiveLogonRight: types.#Kind & { | ||
| symbol: "RemoteInteractiveLogonRight" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| AssignPrimaryTokenPrivilege: types.#Kind & { | ||
| symbol: "AssignPrimaryTokenPrivilege" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| BackupPrivilege: types.#Kind & { | ||
| symbol: "BackupPrivilege" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| CreateTokenPrivilege: types.#Kind & { | ||
| symbol: "CreateTokenPrivilege" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| DebugPrivilege: types.#Kind & { | ||
| symbol: "DebugPrivilege" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| ImpersonatePrivilege: types.#Kind & { | ||
| symbol: "ImpersonatePrivilege" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| LoadDriverPrivilege: types.#Kind & { | ||
| symbol: "LoadDriverPrivilege" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| ManageVolumePrivilege: types.#Kind & { | ||
| symbol: "ManageVolumePrivilege" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| RestorePrivilege: types.#Kind & { | ||
| symbol: "RestorePrivilege" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| TakeOwnershipPrivilege: types.#Kind & { | ||
| symbol: "TakeOwnershipPrivilege" | ||
| schema: "active_directory" | ||
| } | ||
|
|
||
| TcbPrivilege: types.#Kind & { | ||
| symbol: "TcbPrivilege" | ||
| schema: "active_directory" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New privilege kinds defined but not wired into RelationshipKinds.
Without adding these kinds to RelationshipKinds, they won’t appear as edges anywhere. Wire them in and regenerate Go.
🤖 Prompt for AI Agents
In packages/cue/bh/ad/ad.cue around lines 1469 to 1527, several new privilege
kinds (InteractiveLogonRight, RemoteInteractiveLogonRight,
AssignPrimaryTokenPrivilege, BackupPrivilege, CreateTokenPrivilege,
DebugPrivilege, ImpersonatePrivilege, LoadDriverPrivilege,
ManageVolumePrivilege, RestorePrivilege, TakeOwnershipPrivilege, TcbPrivilege)
are defined but not added to RelationshipKinds; update the RelationshipKinds
definition to include these new kinds so they are exposed as edges, then run the
code generation step to regenerate the Go artifacts.
| DebugPrivilege, | ||
| CreateTokenPrivilege, | ||
| SyncLAPSPassword, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
��� Analysis chain
Add missing local-privilege relationships to RelationshipKinds (and then regenerate).
Currently only DebugPrivilege and CreateTokenPrivilege are listed. Add the rest (and optionally InteractiveLogonRight if desired). This keeps CUE → Go → TS in sync and enables ingestion/UI.
Apply this diff:
--- a/packages/cue/bh/ad/ad.cue
+++ b/packages/cue/bh/ad/ad.cue
@@
MemberOfLocalGroup,
- RemoteInteractiveLogonRight,
- DebugPrivilege,
- CreateTokenPrivilege,
+ RemoteInteractiveLogonRight,
+ InteractiveLogonRight, // if you want local interactive explicitly modeled
+ AssignPrimaryTokenPrivilege,
+ BackupPrivilege,
+ CreateTokenPrivilege,
+ DebugPrivilege,
+ ImpersonatePrivilege,
+ LoadDriverPrivilege,
+ ManageVolumePrivilege,
+ RestorePrivilege,
+ TakeOwnershipPrivilege,
+ TcbPrivilege,
SyncLAPSPassword,Then regenerate the Go schema so packages/go/graphschema/ad/ad.go:Relationships() reflects this set.
I'll now generate verification shell scripts to examine the current state of the CUE file and validate the review comment.
🏁 Script executed:
#!/bin/bash
# Examine the CUE file around the specified lines
echo "=== Checking CUE file at packages/cue/bh/ad/ad.cue ==="
if [ -f "packages/cue/bh/ad/ad.cue" ]; then
echo "File exists. Showing lines 1800-1830:"
sed -n '1800,1830p' "packages/cue/bh/ad/ad.cue" | cat -n
else
echo "CUE file not found at expected path"
fi
echo ""
echo "=== Searching for RelationshipKinds in CUE file ==="
grep -n "RelationshipKinds" "packages/cue/bh/ad/ad.cue" | head -20
echo ""
echo "=== Checking for Go schema file ==="
if [ -f "packages/go/graphschema/ad/ad.go" ]; then
echo "Go file exists. Checking Relationships() function:"
grep -n "Relationships()" "packages/go/graphschema/ad/ad.go" -A 30 | head -40
else
echo "Go schema file not found at expected path"
find . -name "ad.go" -path "*/graphschema/*" 2>/dev/null
fi
echo ""
echo "=== Searching for privilege definitions in CUE ==="
rg -n "(Privilege|Right)" "packages/cue/bh/ad/ad.cue" --type cue | grep -E "(Debug|CreateToken|SyncLAPS|Interactive|Assign|Backup|Impersonate|LoadDriver|ManageVolume|Restore|TakeOwnership|Tcb)" | head -20Length of output: 7344
Let me search for more context about these privileges and any generation scripts:
🏁 Script executed:
#!/bin/bash
# Search for documentation or context about these privileges
echo "=== Searching for privilege definitions/comments in CUE file ==="
grep -n -B 5 -A 5 "Privilege\|LogonRight" "packages/cue/bh/ad/ad.cue" | grep -E "(type|#|comment|//" | head -30
echo ""
echo "=== Looking for generation scripts or comments about regeneration ==="
find . -type f \( -name "*.sh" -o -name "*.go" -o -name "*.py" -o -name "*.ts" -o -name "*.cue" \) | xargs grep -l "schema.*gen\|cue.*go\|generate" 2>/dev/null | head -10
echo ""
echo "=== Checking if there's a generate directive or comments in the Go file ==="
head -50 "packages/go/graphschema/ad/ad.go" | grep -E "//go:generate|generated|code gen"
echo ""
echo "=== Searching for any TypeScript schema definitions ==="
find . -path "*/graphschema/*" -name "*.ts" 2>/dev/null | head -5
echo ""
echo "=== Looking for additional context in CUE file around RelationshipKinds ==="
sed -n '1770,1900p' "packages/cue/bh/ad/ad.cue" | head -100Length of output: 2431
Let me verify if these privilege names are referenced elsewhere and check for other schema files:
🏁 Script executed:
#!/bin/bash
# Search for any references to the 9 missing privileges
echo "=== Searching for references to missing privileges ==="
for priv in "InteractiveLogonRight" "AssignPrimaryTokenPrivilege" "BackupPrivilege" "ImpersonatePrivilege" "LoadDriverPrivilege" "ManageVolumePrivilege" "RestorePrivilege" "TakeOwnershipPrivilege" "TcbPrivilege"; do
echo "Searching for: $priv"
rg -n "$priv" --type-list 2>/dev/null | head -1 || echo " (no matches)"
done
echo ""
echo "=== Checking for TypeScript or OpenAPI schema files ==="
find . -path "*/schema*" -type f \( -name "*.ts" -o -name "*.json" -o -name "*.yaml" -o -name "*.yml" \) 2>/dev/null | grep -v node_modules | head -20
echo ""
echo "=== Checking for any documentation about these privileges ==="
find . -type f \( -name "*.md" -o -name "*.txt" -o -name "*.rst" \) | xargs grep -l "Privilege\|LogonRight" 2>/dev/null | head -5
echo ""
echo "=== Verifying the exact diff location in CUE file ==="
sed -n '1810,1825p' "packages/cue/bh/ad/ad.cue" | cat -nLength of output: 2347
Add the 9 missing local-privilege relationships to RelationshipKinds in packages/cue/bh/ad/ad.cue (lines 1815–1817 region), then regenerate the Go schema.
Currently only DebugPrivilege and CreateTokenPrivilege are included. The following privileges are missing and should be added in alphabetical order:
- AssignPrimaryTokenPrivilege
- BackupPrivilege
- ImpersonatePrivilege
- InteractiveLogonRight
- LoadDriverPrivilege
- ManageVolumePrivilege
- RestorePrivilege
- TakeOwnershipPrivilege
- TcbPrivilege
After adding these to the CUE file, regenerate packages/go/graphschema/ad/ad.go so the Relationships() function reflects the complete set. This ensures CUE → Go → TS consistency for ingestion and UI.
| privilegeMap := map[string]graph.Kind{ | ||
| UserRightInteractiveLogon: ad.InteractiveLogonRight, | ||
| UserRightRemoteInteractiveLogon: ad.RemoteInteractiveLogonRight, | ||
| UserRightAssignPrimaryTokenPrivilege: ad.AssignPrimaryTokenPrivilege, | ||
| UserRightBackupPrivilege: ad.BackupPrivilege, | ||
| UserRightCreateTokenPrivilege: ad.CreateTokenPrivilege, | ||
| UserRightDebugPrivilege: ad.DebugPrivilege, | ||
| UserRightImpersonatePrivilege: ad.ImpersonatePrivilege, | ||
| UserRightLoadDriverPrivilege: ad.LoadDriverPrivilege, | ||
| UserRightManageVolumePrivilege: ad.ManageVolumePrivilege, | ||
| UserRightRestorePrivilege: ad.RestorePrivilege, | ||
| UserRightTakeOwnershipPrivilege: ad.TakeOwnershipPrivilege, | ||
| UserRightTcbPrivilege: ad.TcbPrivilege, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Confirm schema coverage for all mapped edge kinds
Ensure every mapped edge kind here is declared and exported in packages/go/graphschema/ad/ad.go and included in Relationships(). If you want these edges traversed in shortest-path queries, also ensure they’re in the ActiveDirectoryPathfindingEdges set.
Run:
🏁 Script executed:
#!/bin/bash
# Verify new privilege kinds are listed in graphschema and used by UI pathfinding
set -euo pipefail
echo "Checking graphschema/ad/ad.go Relationships() includes all privilege edges..."
edges=(
InteractiveLogonRight
RemoteInteractiveLogonRight
AssignPrimaryTokenPrivilege
BackupPrivilege
CreateTokenPrivilege
DebugPrivilege
ImpersonatePrivilege
LoadDriverPrivilege
ManageVolumePrivilege
RestorePrivilege
TakeOwnershipPrivilege
TcbPrivilege
)
block="$(rg -nPU '(?s)func\s+Relationships\(\)\s*\[\]graph\.Kind\s*\{.*?return\s*\[\]graph\.Kind\{.*?\}\s*\}' packages/go/graphschema/ad/ad.go -o | sed -E 's/^:[0-9]+://' )"
missing=()
for e in "${edges[@]}"; do
grep -q "$e" <<<"$block" || missing+=("$e")
done
if ((${#missing[@]})); then
echo "Missing in Relationships(): ${missing[*]}"
exit 1
else
echo "All edges present in Relationships()."
fi
echo "Checking UI pathfinding edges (graphSchema.ts) include new privilege edges (if intended)..."
ui_file="$(fd -t f graphSchema.ts packages/javascript | head -1 || true)"
if [[ -n "$ui_file" ]]; then
ui_block="$(rg -nPU '(?s)ActiveDirectoryPathfindingEdges\s*\(\)\s*{.*?return\s*\[.*?\];\s*}' "$ui_file" -o | sed -E 's/^:[0-9]+://')"
ui_missing=()
for e in "${edges[@]}"; do
grep -q "$e" <<<"$ui_block" || ui_missing+=("$e")
done
if ((${#ui_missing[@]})); then
echo "Missing from ActiveDirectoryPathfindingEdges(): ${ui_missing[*]}"
exit 2
else
echo "All edges present in ActiveDirectoryPathfindingEdges()."
fi
else
echo "UI graphSchema.ts not found; skip."
fiLength of output: 286
🏁 Script executed:
#!/bin/bash
# Get the actual state of Relationships() in graphschema to show what's currently there
echo "Current content of Relationships() function:"
rg -nPU '(?s)func\s+Relationships\(\)\s*\[\]graph\.Kind\s*\{.*?return\s*\[\]graph\.Kind\{(.*?)\}\s*\}' packages/go/graphschema/ad/ad.go -o | head -50Length of output: 1462
🏁 Script executed:
#!/bin/bash
# Double-check the exact kind definitions in ad.go to see what's exported
echo "Checking what edge kinds are actually defined in graphschema/ad/ad.go..."
rg -nP '(InteractiveLogonRight|RemoteInteractiveLogonRight|AssignPrimaryTokenPrivilege|BackupPrivilege|CreateTokenPrivilege|DebugPrivilege|ImpersonatePrivilege|LoadDriverPrivilege|ManageVolumePrivilege|RestorePrivilege|TakeOwnershipPrivilege|TcbPrivilege)' packages/go/graphschema/ad/ad.goLength of output: 2374
🏁 Script executed:
#!/bin/bash
# Find where these kinds should be defined/added
echo "Finding const or var declarations in ad.go:"
fd -t f ad.go packages/go/graphschema | head -5 | xargs -I {} sh -c 'echo "File: {}"; cat {}'Length of output: 53601
🏁 Script executed:
#!/bin/bash
# Check if types are defined by looking for their declaration patterns
echo "Searching for type definitions of the privilege kinds..."
rg -n "const\s+\(|var\s+\(" packages/go/graphschema/ad/ad.go | head -20Length of output: 77
Add missing privilege edges to Relationships() in packages/go/graphschema/ad/ad.go
10 privilege edges are defined (lines 82–93) but missing from the Relationships() function (line 1160–1162): InteractiveLogonRight, AssignPrimaryTokenPrivilege, BackupPrivilege, CreateTokenPrivilege, ImpersonatePrivilege, LoadDriverPrivilege, ManageVolumePrivilege, RestorePrivilege, TakeOwnershipPrivilege, TcbPrivilege. Add all 12 privilege edges to Relationships() and evaluate whether they should also be added to PathfindingRelationships() depending on whether shortest-path queries should traverse them.
| } | ||
| func Relationships() []graph.Kind { | ||
| return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, Contains, GPLink, AllowedToDelegate, CoerceToTGT, GetChanges, GetChangesAll, GetChangesInFilteredSet, CrossForestTrust, SameForestTrust, SpoofSIDHistory, AbuseTGTDelegation, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, LocalToComputer, MemberOfLocalGroup, RemoteInteractiveLogonRight, SyncLAPSPassword, WriteAccountRestrictions, WriteGPLink, RootCAFor, DCFor, PublishedTo, ManageCertificates, ManageCA, DelegatedEnrollmentAgent, Enroll, HostsCAService, WritePKIEnrollmentFlag, WritePKINameFlag, NTAuthStoreFor, TrustedForNTAuth, EnterpriseCAFor, IssuedSignedBy, GoldenCert, EnrollOnBehalfOf, OIDGroupLink, ExtendedByPolicy, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC6a, ADCSESC6b, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC13, SyncedToEntraUser, CoerceAndRelayNTLMToSMB, CoerceAndRelayNTLMToADCS, WriteOwnerLimitedRights, WriteOwnerRaw, OwnsLimitedRights, OwnsRaw, ClaimSpecialIdentity, CoerceAndRelayNTLMToLDAP, CoerceAndRelayNTLMToLDAPS, ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO, HasTrustKeys, ProtectAdminGroups} | ||
| return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, Contains, GPLink, AllowedToDelegate, CoerceToTGT, GetChanges, GetChangesAll, GetChangesInFilteredSet, CrossForestTrust, SameForestTrust, SpoofSIDHistory, AbuseTGTDelegation, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, LocalToComputer, MemberOfLocalGroup, RemoteInteractiveLogonRight, DebugPrivilege, SyncLAPSPassword, WriteAccountRestrictions, WriteGPLink, RootCAFor, DCFor, PublishedTo, ManageCertificates, ManageCA, DelegatedEnrollmentAgent, Enroll, HostsCAService, WritePKIEnrollmentFlag, WritePKINameFlag, NTAuthStoreFor, TrustedForNTAuth, EnterpriseCAFor, IssuedSignedBy, GoldenCert, EnrollOnBehalfOf, OIDGroupLink, ExtendedByPolicy, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC6a, ADCSESC6b, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, ADCSESC13, SyncedToEntraUser, CoerceAndRelayNTLMToSMB, CoerceAndRelayNTLMToADCS, WriteOwnerLimitedRights, WriteOwnerRaw, OwnsLimitedRights, OwnsRaw, ClaimSpecialIdentity, CoerceAndRelayNTLMToLDAP, CoerceAndRelayNTLMToLDAPS, ContainsIdentity, PropagatesACEsTo, GPOAppliesTo, CanApplyGPO, HasTrustKeys, ProtectAdminGroups} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Schema mismatch: CreateTokenPrivilege missing from Relationships(); others not exposed.
CUE adds CreateTokenPrivilege (and DebugPrivilege) to RelationshipKinds, but this Go list only includes DebugPrivilege. This will drop CreateTokenPrivilege edges at runtime.
- Do not hand-edit this file; add the missing relationships in CUE and regenerate.
Apply in packages/cue/bh/ad/ad.cue (see dedicated comment) and then regenerate to sync this function.
Run to compare TS vs Go vs CUE relationship sets:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "TS ActiveDirectoryRelationshipKind values:"
rg -nU 'export enum ActiveDirectoryRelationshipKind\s*{(?s).*?^}' packages/javascript/bh-shared-ui/src/graphSchema.ts | sed -n '/ActiveDirectoryRelationshipKind {/,/}/{p}' | rg "^\s*[A-Za-z0-9_]+ =" -n | sed 's/.*ActiveDirectoryRelationshipKind\.//g;s/\s*=.*//g' | sort -u
echo
echo "Go ad.Relationships() values:"
rg -nU 'func Relationships\(\).*?return \[\]graph\.Kind{(?s).*?}' packages/go/graphschema/ad/ad.go | sed -n '/return \[\]graph.Kind{/,/}/{p}' | tr -d ' ' | tr ',' '\n' | rg '^[A-Za-z0-9]+' | sed 's/}//g' | sort -u
echo
echo "CUE RelationshipKinds values:"
sed -n '/RelationshipKinds:\s*\[/,/]/p' packages/cue/bh/ad/ad.cue | rg '^\s*[A-Za-z0-9]+' -n | sed 's/^[[:space:]]\+//g;s/,//g' | sort -u
echo
echo "Diff (TS - Go):"
comm -23 <(rg -nU 'export enum ActiveDirectoryRelationshipKind\s*{(?s).*?^}' packages/javascript/bh-shared-ui/src/graphSchema.ts | sed -n '/ActiveDirectoryRelationshipKind {/,/}/{p}' | rg "^\s*[A-Za-z0-9_]+ =" -n | sed 's/.*ActiveDirectoryRelationshipKind\.//g;s/\s*=.*//g' | sort -u) \
<(rg -nU 'func Relationships\(\).*?return \[\]graph\.Kind{(?s).*?}' packages/go/graphschema/ad/ad.go | sed -n '/return \[\]graph.Kind{/,/}/{p}' | tr -d ' ' | tr ',' '\n' | rg '^[A-Za-z0-9]+' | sed 's/}//g' | sort -u)
echo
echo "Diff (CUE - Go):"
comm -23 <(sed -n '/RelationshipKinds:\s*\[/,/]/p' packages/cue/bh/ad/ad.cue | rg '^\s*[A-Za-z0-9]+' -n | sed 's/^[[:space:]]\+//g;s/,//g' | sort -u) \
<(rg -nU 'func Relationships\(\).*?return \[\]graph\.Kind{(?s).*?}' packages/go/graphschema/ad/ad.go | sed -n '/return \[\]graph.Kind{/,/}/{p}' | tr -d ' ' | tr ',' '\n' | rg '^[A-Za-z0-9]+' | sed 's/}//g' | sort -u)Length of output: 228
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== TS ActiveDirectoryRelationshipKind values ==="
rg 'export enum ActiveDirectoryRelationshipKind' -A 200 packages/javascript/bh-shared-ui/src/graphSchema.ts | grep -E '^\s+[A-Za-z]+\s*=' | sed 's/.*=//;s/[,;].*//' | tr -d ' ' | sort -u
echo
echo "=== Go ad.Relationships() values ==="
rg 'func Relationships\(\)' -A 5 packages/go/graphschema/ad/ad.go | grep 'return \[\]' -A 1 | tr ',' '\n' | tr -d ' {}[]' | grep -E '^[A-Za-z]' | sort -u
echo
echo "=== CUE RelationshipKinds values ==="
rg 'RelationshipKinds:' -A 200 packages/cue/bh/ad/ad.cue | sed -n '/RelationshipKinds:/,/]/p' | grep -E '^\s+[A-Za-z_]+' | sed 's/^[[:space:]]*//;s/[,].*//' | sort -u
echo
echo "=== Relationships in TS but NOT in Go ==="
comm -23 <(rg 'export enum ActiveDirectoryRelationshipKind' -A 200 packages/javascript/bh-shared-ui/src/graphSchema.ts | grep -E '^\s+[A-Za-z]+\s*=' | sed 's/.*=//;s/[,;].*//' | tr -d ' ' | sort -u) \
<(rg 'func Relationships\(\)' -A 5 packages/go/graphschema/ad/ad.go | grep 'return \[\]' -A 1 | tr ',' '\n' | tr -d ' {}[]' | grep -E '^[A-Za-z]' | sort -u)
echo
echo "=== Relationships in CUE but NOT in Go ==="
comm -23 <(rg 'RelationshipKinds:' -A 200 packages/cue/bh/ad/ad.cue | sed -n '/RelationshipKinds:/,/]/p' | grep -E '^\s+[A-Za-z_]+' | sed 's/^[[:space:]]*//;s/[,].*//' | sort -u) \
<(rg 'func Relationships\(\)' -A 5 packages/go/graphschema/ad/ad.go | grep 'return \[\]' -A 1 | tr ',' '\n' | tr -d ' {}[]' | grep -E '^[A-Za-z]' | sort -u)Length of output: 4282
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Extract and clean the lists for easy comparison
echo "=== CRITICAL FINDING: Relationships in CUE but NOT in Go ==="
cat > /tmp/cue_list.txt << 'EOF'
ADCSESC1
ADCSESC10a
ADCSESC10b
ADCSESC13
ADCSESC3
ADCSESC4
ADCSESC6a
ADCSESC6b
ADCSESC9a
ADCSESC9b
AbuseTGTDelegation
AddAllowedToAct
AddKeyCredentialLink
AddMember
AddSelf
AdminTo
AllExtendedRights
AllowedToAct
AllowedToDelegate
CanApplyGPO
CanPSRemote
CanRDP
ClaimSpecialIdentity
CoerceAndRelayNTLMToADCS
CoerceAndRelayNTLMToLDAP
CoerceAndRelayNTLMToLDAPS
CoerceAndRelayNTLMToSMB
CoerceToTGT
Contains
ContainsIdentity
CreateTokenPrivilege
CrossForestTrust
DCFor
DCSync
DebugPrivilege
DelegatedEnrollmentAgent
DumpSMSAPassword
Enroll
EnrollOnBehalfOf
EnterpriseCAFor
ExecuteDCOM
ExtendedByPolicy
ForceChangePassword
GPLink
GPOAppliesTo
GenericAll
GenericWrite
GetChanges
GetChangesAll
GetChangesInFilteredSet
GoldenCert
HasSIDHistory
HasSession
HasTrustKeys
HostsCAService
IssuedSignedBy
LocalToComputer
ManageCA
ManageCertificates
MemberOf
MemberOfLocalGroup
NTAuthStoreFor
OIDGroupLink
Owns
OwnsLimitedRights
OwnsRaw
PropagatesACEsTo
ProtectAdminGroups
PublishedTo
ReadGMSAPassword
ReadLAPSPassword
RemoteInteractiveLogonRight
RootCAFor
SQLAdmin
SameForestTrust
SpoofSIDHistory
SyncLAPSPassword
SyncedToEntraUser
TrustedForNTAuth
WriteAccountRestrictions
WriteDACL
WriteGPLink
WriteOwner
WriteOwnerLimitedRights
WriteOwnerRaw
WritePKIEnrollmentFlag
WritePKINameFlag
WriteSPN
EOF
cat > /tmp/go_list.txt << 'EOF'
ADCSESC1
ADCSESC10a
ADCSESC10b
ADCSESC13
ADCSESC3
ADCSESC4
ADCSESC6a
ADCSESC6b
ADCSESC9a
ADCSESC9b
AbuseTGTDelegation
AddAllowedToAct
AddKeyCredentialLink
AddMember
AddSelf
AdminTo
AllExtendedRights
AllowedToAct
AllowedToDelegate
CanApplyGPO
CanPSRemote
CanRDP
ClaimSpecialIdentity
CoerceAndRelayNTLMToADCS
CoerceAndRelayNTLMToLDAP
CoerceAndRelayNTLMToLDAPS
CoerceAndRelayNTLMToSMB
CoerceToTGT
Contains
ContainsIdentity
CrossForestTrust
DCFor
DCSync
DebugPrivilege
DelegatedEnrollmentAgent
DumpSMSAPassword
Enroll
EnrollOnBehalfOf
EnterpriseCAFor
ExecuteDCOM
ExtendedByPolicy
ForceChangePassword
GPLink
GPOAppliesTo
GenericAll
GenericWrite
GetChanges
GetChangesAll
GetChangesInFilteredSet
GoldenCert
HasSIDHistory
HasSession
HasTrustKeys
HostsCAService
IssuedSignedBy
LocalToComputer
ManageCA
ManageCertificates
MemberOf
MemberOfLocalGroup
NTAuthStoreFor
OIDGroupLink
Owns
OwnsLimitedRights
OwnsRaw
PropagatesACEsTo
ProtectAdminGroups
PublishedTo
ReadGMSAPassword
ReadLAPSPassword
RemoteInteractiveLogonRight
RootCAFor
SQLAdmin
SameForestTrust
SpoofSIDHistory
SyncLAPSPassword
SyncedToEntraUser
TrustedForNTAuth
WriteAccountRestrictions
WriteDACL
WriteGPLink
WriteOwner
WriteOwnerLimitedRights
WriteOwnerRaw
WritePKIEnrollmentFlag
WritePKINameFlag
WriteSPN
EOF
grep -Fvxf /tmp/go_list.txt /tmp/cue_list.txt
echo
echo "=== Relationships in TS but NOT in Go (sample check) ==="
grep -i "CreateTokenPrivilege\|AssignPrimaryTokenPrivilege\|BackupPrivilege" << 'TSLIST'
AbuseTGTDelegation
AddAllowedToAct
AddKeyCredentialLink
AddMember
AddSelf
AdminTo
AllExtendedRights
AllowedToAct
AllowedToDelegate
AssignPrimaryTokenPrivilege
BackupPrivilege
CanApplyGPO
CanPSRemote
CanRDP
ClaimSpecialIdentity
CoerceAndRelayNTLMToADCS
CoerceAndRelayNTLMToLDAP
CoerceAndRelayNTLMToLDAPS
CoerceAndRelayNTLMToSMB
CoerceToTGT
Contains
ContainsIdentity
CreateTokenPrivilege
CrossForestTrust
DCFor
DCSync
DebugPrivilege
DelegatedEnrollmentAgent
DumpSMSAPassword
Enroll
EnrollOnBehalfOf
EnterpriseCAFor
ExecuteDCOM
ExtendedByPolicy
ForceChangePassword
GPLink
GPOAppliesTo
GenericAll
GenericWrite
GetChanges
GetChangesAll
GetChangesInFilteredSet
GoldenCert
HasSIDHistory
HasSession
HasTrustKeys
HostsCAService
ImpersonatePrivilege
InteractiveLogonRight
IssuedSignedBy
LoadDriverPrivilege
LocalToComputer
ManageCA
ManageCertificates
ManageVolumePrivilege
MemberOf
MemberOfLocalGroup
NTAuthStoreFor
OIDGroupLink
Owns
OwnsLimitedRights
OwnsRaw
PropagatesACEsTo
ProtectAdminGroups
PublishedTo
ReadGMSAPassword
ReadLAPSPassword
RemoteInteractiveLogonRight
RestorePrivilege
RootCAFor
SQLAdmin
SameForestTrust
SpoofSIDHistory
SyncLAPSPassword
SyncedToEntraUser
TakeOwnershipPrivilege
TcbPrivilege
TrustedForNTAuth
WriteAccountRestrictions
WriteDacl
WriteGPLink
WriteOwner
WriteOwnerLimitedRights
WriteOwnerRaw
WritePKIEnrollmentFlag
WritePKINameFlag
WriteSPN
TSLISTLength of output: 204
Confirmed: CreateTokenPrivilege missing from Go Relationships(); needs regeneration from CUE.
CUE includes CreateTokenPrivilege in RelationshipKinds, but the Go Relationships() function at line 1161 does not. This will drop CreateTokenPrivilege edges at runtime. Regenerate this file from the CUE source to sync.
🤖 Prompt for AI Agents
packages/go/graphschema/ad/ad.go around line 1161: the Relationships() return
slice is missing CreateTokenPrivilege (present in the CUE RelationshipKinds) so
runtime edges are dropped; regenerate this Go file from the CUE source (or
re-run the codegen that emits RelationshipKinds) so the returned []graph.Kind
includes CreateTokenPrivilege, then run tests/build, verify the generated list
contains CreateTokenPrivilege, and commit the regenerated file.
| name: 'Computers where local privilege escalation is possible through users\'s privileges', | ||
| description: '', | ||
| query: `MATCH p = (a)-[:AssignPrimaryTokenPrivilege|BackupPrivilege|CreateTokenPrivilege|DebugPrivilege|ImpersonatePrivilege|LoadDriverPrivilege|ManageVolumePrivilege|RestorePrivilege|TakeOwnershipPrivilege|TcbPrivilege]->(c:Computer)\nRETURN p LIMIT 500`, | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix possessive typo in query name
Use “users’ privileges” (plural possessive) instead of “users’s”.
Apply:
- name: 'Computers where local privilege escalation is possible through users\'s privileges',
+ name: 'Computers where local privilege escalation is possible through users\' privileges',📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| name: 'Computers where local privilege escalation is possible through users\'s privileges', | |
| description: '', | |
| query: `MATCH p = (a)-[:AssignPrimaryTokenPrivilege|BackupPrivilege|CreateTokenPrivilege|DebugPrivilege|ImpersonatePrivilege|LoadDriverPrivilege|ManageVolumePrivilege|RestorePrivilege|TakeOwnershipPrivilege|TcbPrivilege]->(c:Computer)\nRETURN p LIMIT 500`, | |
| }, | |
| name: 'Computers where local privilege escalation is possible through users\' privileges', | |
| description: '', | |
| query: `MATCH p = (a)-[:AssignPrimaryTokenPrivilege|BackupPrivilege|CreateTokenPrivilege|DebugPrivilege|ImpersonatePrivilege|LoadDriverPrivilege|ManageVolumePrivilege|RestorePrivilege|TakeOwnershipPrivilege|TcbPrivilege]->(c:Computer)\nRETURN p LIMIT 500`, | |
| }, |
🤖 Prompt for AI Agents
In packages/javascript/bh-shared-ui/src/commonSearchesAGT.ts around lines 587 to
590, the query name contains a possessive typo "users's privileges"; change it
to the correct plural possessive "users' privileges" so the string becomes
"Computers where local privilege escalation is possible through users'
privileges".
| InteractiveLogonRight = 'InteractiveLogonRight', | ||
| RemoteInteractiveLogonRight = 'RemoteInteractiveLogonRight', | ||
| AssignPrimaryTokenPrivilege = 'AssignPrimaryTokenPrivilege', | ||
| BackupPrivilege = 'BackupPrivilege', | ||
| CreateTokenPrivilege = 'CreateTokenPrivilege', | ||
| DebugPrivilege = 'DebugPrivilege', | ||
| ImpersonatePrivilege = 'ImpersonatePrivilege', | ||
| LoadDriverPrivilege = 'LoadDriverPrivilege', | ||
| ManageVolumePrivilege = 'ManageVolumePrivilege', | ||
| RestorePrivilege = 'RestorePrivilege', | ||
| TakeOwnershipPrivilege = 'TakeOwnershipPrivilege', | ||
| TcbPrivilege = 'TcbPrivilege', | ||
| SyncLAPSPassword = 'SyncLAPSPassword', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
UI-backend parity: UI lists 11 new local-privilege edges, backend exposes 1–2.
TS adds 11 kinds and display cases, but Go Relationships() currently includes RemoteInteractiveLogonRight and DebugPrivilege only (CreateTokenPrivilege also intended in CUE). This mismatch can surface edges the API never returns.
- Either narrow the TS enum to the currently supported set, or finish wiring all intended kinds in CUE RelationshipKinds and regenerate Go.
- After backend alignment, consider whether these edges should be included in pathfinding arrays if they must influence routes.
I can generate a follow-up PR to align both sides once you confirm the desired set.
Also applies to: 249-272
🏁 Script executed:
#!/bin/bash
# Examine full context of graphSchema.ts around the reported lines
echo "=== graphSchema.ts lines 100-130 ==="
find . -name "graphSchema.ts" -type f | head -1 | xargs sed -n '100,130p'
echo -e "\n=== graphSchema.ts lines 240-280 ==="
find . -name "graphSchema.ts" -type f | head -1 | xargs sed -n '240,280p'Length of output: 3525
🏁 Script executed:
#!/bin/bash
# Find Go files containing Relationships function
echo "=== Searching for Go Relationships() function ==="
rg -n "func.*Relationships" --type=go -A 20
# Find CUE files with RelationshipKinds
echo -e "\n=== Searching for RelationshipKinds in CUE files ==="
rg -n "RelationshipKinds" --type-not=ts --type-not=js -B 2 -A 10Length of output: 145935
🏁 Script executed:
#!/bin/bash
# Count privilege-related enum values in graphSchema.ts
echo "=== Counting InteractiveLogonRight through SyncLAPSPassword in TS ==="
find . -name "graphSchema.ts" -type f | head -1 | xargs grep -n "Privilege\|LogonRight\|SyncLAPSPassword" | head -20Length of output: 1214
UI-backend relationship kind mismatch: TS enum includes 10 kinds not exposed by Go Relationships().
TS enum lines 111-123 adds 13 privilege/logon-right kinds. Go Relationships() in packages/go/graphschema/ad/ad.go:1160 exposes only 3 of them: RemoteInteractiveLogonRight, DebugPrivilege, and SyncLAPSPassword. Missing from backend: InteractiveLogonRight, AssignPrimaryTokenPrivilege, BackupPrivilege, CreateTokenPrivilege, ImpersonatePrivilege, LoadDriverPrivilege, ManageVolumePrivilege, RestorePrivilege, TakeOwnershipPrivilege, TcbPrivilege.
Frontend will advertise edges that the API cannot return, causing display or search failures.
Action required:
- Either remove the 10 unsupported kinds from the TS enum and display mapping, or
- Complete the CUE RelationshipKinds definition (packages/cue/bh/ad/ad.cue:1775) to include all 13 kinds and regenerate Go code.
Align both sides before merging to prevent runtime "unknown edge" issues.
🤖 Prompt for AI Agents
In packages/javascript/bh-shared-ui/src/graphSchema.ts around lines 111-123 the
TypeScript enum adds 13 privilege/logon-right kinds but the Go backend
(packages/go/graphschema/ad/ad.go:1160) only exposes 3, causing the frontend to
advertise edges the API cannot return; fix by either removing the 10 unsupported
kinds from this TS enum and any display/mapping that references them
(InteractiveLogonRight, AssignPrimaryTokenPrivilege, BackupPrivilege,
CreateTokenPrivilege, ImpersonatePrivilege, LoadDriverPrivilege,
ManageVolumePrivilege, RestorePrivilege, TakeOwnershipPrivilege, TcbPrivilege)
or by adding all 13 kinds into the CUE RelationshipKinds definition at
packages/cue/bh/ad/ad.cue:1775 and regenerating the Go code so
packages/go/graphschema/ad/ad.go includes all new kinds (then run codegen/build
to propagate changes); ensure the final change keeps TS enum and Go
Relationships() in exact sync before merging.
Description
This pull request add the ability to BloodHound-CE to support more edges related to local privileges via access tokens.
I have also made changes to SharpHound and BloodHound.py to support this feature.
Motivation and Context
Resolves #1998.
Why is this change required? What problem does it solve?
This allows BloodHound-CE users to quickly figure out where users have high impact local privilege on computers to better anticipate local privilege escalation.
How Has This Been Tested?
Please describe in detail how you tested your changes.
Include details of your testing environment, and the tests you ran to
see how your change affects other areas of the code, etc.
The changes have been tested manually on a lab environment. I did not change much of the code and successfully run the tests.
Screenshots (optional):
Types of changes
Checklist:
Summary by CodeRabbit