Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions cmd/api/src/services/graphify/convertors.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,48 @@ func convertComputerData(computer ein.Computer, converted *ConvertedData, ingest
continue
}

if userRight.Privilege == ein.UserRightRemoteInteractiveLogon {
switch userRight.Privilege {
case ein.UserRightInteractiveLogon:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.InteractiveLogonRight)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightRemoteInteractiveLogon:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.RemoteInteractiveLogonRight)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightAssignPrimaryTokenPrivilege:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.AssignPrimaryTokenPrivilege)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightBackupPrivilege:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.BackupPrivilege)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightCreateTokenPrivilege:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.CreateTokenPrivilege)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightDebugPrivilege:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.DebugPrivilege)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightImpersonatePrivilege:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.ImpersonatePrivilege)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightLoadDriverPrivilege:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.LoadDriverPrivilege)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightManageVolumePrivilege:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.ManageVolumePrivilege)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightRestorePrivilege:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.RestorePrivilege)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightTakeOwnershipPrivilege:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.TakeOwnershipPrivilege)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
case ein.UserRightTcbPrivilege:
converted.RelProps = append(converted.RelProps, ein.ParseUserRightData(userRight, computer, ad.TcbPrivilege)...)
baseNodeProp.PropertyMap[ad.HasURA.String()] = true
}
}

converted.NodeProps = append(converted.NodeProps, ein.ParseDCRegistryData(computer))
converted.NodeProps = append(converted.NodeProps, baseNodeProp)

}

func convertUserData(user ein.User, converted *ConvertedData, ingestTime time.Time) {
Expand Down Expand Up @@ -192,6 +225,10 @@ func convertOUData(ou ein.OU, converted *ConvertedData, ingestTime time.Time) {
parsedLocalGroupData := ein.ParseGPOChanges(ou.GPOChanges)
converted.RelProps = append(converted.RelProps, parsedLocalGroupData.Relationships...)
converted.NodeProps = append(converted.NodeProps, parsedLocalGroupData.Nodes...)

parsedUserRightsData := ein.ParseGPOUserRights(ou.GPOUserRights)
converted.RelProps = append(converted.RelProps, parsedUserRightsData.Relationships...)
converted.NodeProps = append(converted.NodeProps, parsedUserRightsData.Nodes...)
}

func convertSessionData(session ein.Session, converted *ConvertedSessionData) {
Expand Down
57 changes: 57 additions & 0 deletions packages/cue/bh/ad/ad.cue
Original file line number Diff line number Diff line change
Expand Up @@ -1466,11 +1466,66 @@ AddKeyCredentialLink: types.#Kind & {
schema: "active_directory"
}

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"
}
Comment on lines +1469 to +1527
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.


SyncLAPSPassword: types.#Kind & {
symbol: "SyncLAPSPassword"
schema: "active_directory"
Expand Down Expand Up @@ -1757,6 +1812,8 @@ RelationshipKinds: [
LocalToComputer,
MemberOfLocalGroup,
RemoteInteractiveLogonRight,
DebugPrivilege,
CreateTokenPrivilege,
SyncLAPSPassword,
Comment on lines +1815 to 1817
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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 -20

Length 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 -100

Length 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 -n

Length 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.

WriteAccountRestrictions,
WriteGPLink,
Expand Down
66 changes: 66 additions & 0 deletions packages/go/ein/ad.go
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,72 @@ func ParseGPOChanges(changes GPOChanges) ParsedLocalGroupData {
return parsedData
}

func ParseGPOUserRights(privileges GPOUserRights) ParsedUserRightsData {
parsedData := ParsedUserRightsData{}

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,
}
Comment on lines +829 to +842
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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."
fi

Length 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 -50

Length 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.go

Length 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 -20

Length 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.


var privilegesToProcess []Privilege

for privilegeName, members := range privileges.UserRightAssignments {

if _, exists := privilegeMap[privilegeName]; exists && len(members) > 0 {
privilegesToProcess = append(privilegesToProcess, Privilege{
Name: privilegeName,
Members: members,
})
}
}

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(
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,
},
))
}
}
}

return parsedData
}


func ParseGpLinks(links []GPLink, itemIdentifier string, itemType graph.Kind) []IngestibleRelationship {
relationships := make([]IngestibleRelationship, 0, len(links))
for _, gpLink := range links {
Expand Down
34 changes: 28 additions & 6 deletions packages/go/ein/incoming_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,23 @@ import (
)

const (
TrustDirectionDisabled = "Disabled"
TrustDirectionOutbound = "Outbound"
TrustDirectionInbound = "Inbound"
TrustDirectionBidirectional = "Bidirectional"
IgnoredName = "IGNOREME"
UserRightRemoteInteractiveLogon = "SeRemoteInteractiveLogonRight"
TrustDirectionDisabled = "Disabled"
TrustDirectionOutbound = "Outbound"
TrustDirectionInbound = "Inbound"
TrustDirectionBidirectional = "Bidirectional"
IgnoredName = "IGNOREME"
UserRightInteractiveLogon = "SeInteractiveLogonRight"
UserRightRemoteInteractiveLogon = "SeRemoteInteractiveLogonRight"
UserRightAssignPrimaryTokenPrivilege = "SeAssignPrimaryTokenPrivilege"
UserRightBackupPrivilege = "SeBackupPrivilege"
UserRightCreateTokenPrivilege = "SeCreateTokenPrivilege"
UserRightDebugPrivilege = "SeDebugPrivilege"
UserRightImpersonatePrivilege = "SeImpersonatePrivilege"
UserRightLoadDriverPrivilege = "SeLoadDriverPrivilege"
UserRightManageVolumePrivilege = "SeManageVolumePrivilege"
UserRightRestorePrivilege = "SeRestorePrivilege"
UserRightTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
UserRightTcbPrivilege = "SeTcbPrivilege"
)

func parseADKind(rawKindStr string) graph.Kind {
Expand Down Expand Up @@ -356,11 +367,22 @@ type GPOChanges struct {
AffectedComputers []TypedPrincipal
}

type GPOUserRights struct {
AffectedComputers []TypedPrincipal
UserRightAssignments map[string][]TypedPrincipal
}

type Privilege struct {
Name string
Members []TypedPrincipal
}

type OU struct {
IngestBase
ChildObjects []TypedPrincipal
Links []GPLink
GPOChanges GPOChanges
GPOUserRights GPOUserRights
InheritanceHashes []string
}

Expand Down
5 changes: 5 additions & 0 deletions packages/go/ein/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ type ParsedLocalGroupData struct {
Nodes []IngestibleNode
}

type ParsedUserRightsData struct {
Relationships []IngestibleRelationship
Nodes []IngestibleNode
}

type ParsedDomainTrustData struct {
TrustRelationships []IngestibleRelationship
ExtraNodeProps []IngestibleNode
Expand Down
15 changes: 13 additions & 2 deletions packages/go/graphschema/ad/ad.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions packages/javascript/bh-shared-ui/src/commonSearchesAGT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -564,4 +564,30 @@ RETURN p\nLIMIT 1000`,
},
],
},
{
subheader: 'Local Privileges',
category: categoryAD,
queries: [
{
name: 'All Local Privileges edges',
description: '',
query: `MATCH p = (a)-[:InteractiveLogonRight|RemoteInteractiveLogonRight|AssignPrimaryTokenPrivilege|BackupPrivilege|CreateTokenPrivilege|DebugPrivilege|ImpersonatePrivilege|LoadDriverPrivilege|ManageVolumePrivilege|RestorePrivilege|TakeOwnershipPrivilege|TcbPrivilege]->(c:Computer)\nRETURN p LIMIT 500`,
},
{
name: 'Computers that allow local interactive logon',
description: '',
query: `MATCH p = (a)-[:InteractiveLogonRight]->(c:Computer)\nRETURN p LIMIT 500`,
},
{
name: 'Computers that allow remote interactive logon',
description: '',
query: `MATCH p = (a)-[:RemoteInteractiveLogonRight]->(c:Computer)\nRETURN p LIMIT 500`,
},
{
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`,
},
Comment on lines +587 to +590
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
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".

],
},
];
33 changes: 33 additions & 0 deletions packages/javascript/bh-shared-ui/src/graphSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,18 @@ export enum ActiveDirectoryRelationshipKind {
AddKeyCredentialLink = 'AddKeyCredentialLink',
LocalToComputer = 'LocalToComputer',
MemberOfLocalGroup = 'MemberOfLocalGroup',
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',
Comment on lines +111 to 123
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 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 10

Length 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 -20

Length 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.

WriteAccountRestrictions = 'WriteAccountRestrictions',
WriteGPLink = 'WriteGPLink',
Expand Down Expand Up @@ -235,8 +246,30 @@ export function ActiveDirectoryRelationshipKindToDisplay(value: ActiveDirectoryR
return 'LocalToComputer';
case ActiveDirectoryRelationshipKind.MemberOfLocalGroup:
return 'MemberOfLocalGroup';
case ActiveDirectoryRelationshipKind.InteractiveLogonRight:
return 'InteractiveLogonRight';
case ActiveDirectoryRelationshipKind.RemoteInteractiveLogonRight:
return 'RemoteInteractiveLogonRight';
case ActiveDirectoryRelationshipKind.AssignPrimaryTokenPrivilege:
return 'AssignPrimaryTokenPrivilege';
case ActiveDirectoryRelationshipKind.BackupPrivilege:
return 'BackupPrivilege';
case ActiveDirectoryRelationshipKind.CreateTokenPrivilege:
return 'CreateTokenPrivilege';
case ActiveDirectoryRelationshipKind.DebugPrivilege:
return 'DebugPrivilege';
case ActiveDirectoryRelationshipKind.ImpersonatePrivilege:
return 'ImpersonatePrivilege';
case ActiveDirectoryRelationshipKind.LoadDriverPrivilege:
return 'LoadDriverPrivilege';
case ActiveDirectoryRelationshipKind.ManageVolumePrivilege:
return 'ManageVolumePrivilege';
case ActiveDirectoryRelationshipKind.RestorePrivilege:
return 'RestorePrivilege';
case ActiveDirectoryRelationshipKind.TakeOwnershipPrivilege:
return 'TakeOwnershipPrivilege';
case ActiveDirectoryRelationshipKind.TcbPrivilege:
return 'TcbPrivilege';
case ActiveDirectoryRelationshipKind.SyncLAPSPassword:
return 'SyncLAPSPassword';
case ActiveDirectoryRelationshipKind.WriteAccountRestrictions:
Expand Down
Loading