Repository navigation
Expand file tree
/
Copy pathstub_node.py
More file actions
60 lines (50 loc) · 2.53 KB
/
Copy pathstub_node.py
File metadata and controls
60 lines (50 loc) · 2.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# src/openhound_sccm/models/stub_node.py
"""StubNode: a bare backfilled node synthesised for edge endpoints with no real node.
`node_backfill` rows are produced by `transforms._node_backfill` for edge END
endpoints that resolved to a SID/smsid not present in any `node_*` table. Each
row carries the endpoint id and the kind inferred from the edge kind (see
`BACKFILL_END_KIND` in graph.py). AD-principal kinds (User, Group, Computer)
also get a 'Base' kind appended; ambiguous ends land with kind='Base' only.
"""
import logging
from openhound.core.asset import BaseAsset
from openhound.core.models.entries_dataclass import NodeProperties
from pydantic import ConfigDict
from ..graph import SCCMNode, domain_environment_id
logger = logging.getLogger(__name__)
# These kinds represent AD principals and get 'Base' appended per the OpenGraph
# identity model (spec §2 "Root/environment node").
_AD_KINDS = {"User", "Group", "Computer"}
class StubNode(BaseAsset):
"""A bare backfilled node (node_backfill row): id + an inferred kind. AD-principal
kinds also get 'Base'; environmentid is the domain SID when id is a SID, else the id."""
model_config = ConfigDict(populate_by_name=True, extra="ignore")
id: str | None = None
kind: str | None = None
@property
def as_node(self) -> SCCMNode | None:
if not self.id or not self.kind:
logger.warning(
"StubNode: dropping row with missing id/kind (id=%r kind=%r)",
self.id,
self.kind,
)
return None
kinds = [self.kind, "Base"] if self.kind in _AD_KINDS else [self.kind]
env = domain_environment_id(self.id) or self.id
return SCCMNode(
id=self.id,
kinds=kinds,
# No name/displayname. A stub exists precisely because nothing in this collect
# knew what this principal *is*, so it has no name to give -- and these nodes
# ship untagged (ARCHITECTURE 11f) to merge into the native AD graph by id, so
# naming a stub after its own SID would overwrite a real SharpHound label with
# the SID string. Both are null and pruned on emit, leaving BloodHound to
# display the object id when it knows nothing better. This is what
# ARCHITECTURE 11e has always specified a stub to be: "just id, kinds ... and
# environmentid". Ope-15m7.
properties=NodeProperties(name=None, displayname=None, environmentid=env),
)
@property
def edges(self):
return iter(())