Skip to content

bug: unable to create Relationship without a target Fact #3247

@luehm

Description

@luehm

Describe the bug
When creating a Relationship between two facts, the Relationship class only requires a source Fact as documented here and as seen in the constructor here. However when processed by create_relationships and save_fact, processing will error out and cease for the current ability instance if a Relationship with an undefined target (i.e., a Relationship without a target Fact) exists in the provided list.

To Reproduce
Steps to reproduce the behavior:

  1. Using a parser, create a relationship with only a source fact and edge:
  def parse(self, blob):
    relationships = []
    source = "domain.computer.hostname"
    computer_name = "www02"
    edge = "can_delegate_unconstrained"

    relationships.append(
      Relationship(
        source=Fact(source, computer_name),
        edge=edge
      )
    )
    return relationships

Expected behavior
A new fact should be created (if not existing), with an updated relationship with an outbound edge of the type specified:

        {
            "unique": "domain.computer.hostnamewww02",
            "trait": "domain.computer.hostname",
            "name": "domain.computer.hostname",
            "value": "www02",
            "created": "2026-01-28T22:56:27Z",
            "score": 1,
            "source": "0d959eb9-46f3-4160-8d5a-55a12f1e5ef0",
            "origin_type": "LEARNED",
            "links": [
                "43940a78-5fc7-4891-a39a-8c6422784a1a",
                "484f9814-b468-436a-8d2b-9abc92a2bacb"
            ],
            "relationships": [
                "domain.computer.hostname(www02) : has_address : domain.computer.ipaddress(10.100.32.122)",
                "domain.computer.hostname(www02) : has_address : domain.computer.dnshostname(www02.int.example)",
                "domain.computer.hostname(www02) : can_delegate_unconstrained"
            ],
            "limit_count": -1,
            "collected_by": [
                "sontsq",
                "evsuvl"
            ],
            "technique_id": "T1018"
        },

Additional context
Add any other context about the problem here.

Adding in a try / catch block allowed the operation to proceed with the expected Relationships being generated. This also outlines the underlying issue when trying to access trait on a None type object:

Updated create_relationships method:

    async def create_relationships(self, relationships, operation):
        for relationship in relationships:
            relationship.origin = operation.id if operation else self.id
            try:
                await self.save_fact(operation, relationship.source, relationship.score, relationship.shorthand)
                await self.save_fact(operation, relationship.target, relationship.score, relationship.shorthand)
            except Exception as e:
                logging.getLogger("link").warning("Trying to process relationship %s failed: %s",
                                                  relationship.shorthand,
                                                  e)

            if all((relationship.source.trait, relationship.edge)):
                knowledge_svc_handle = BaseService.get_service('knowledge_svc')
                await knowledge_svc_handle.add_relationship(relationship)
                self.relationships.append(relationship)

Resulting log output:

Trying to process relationship domain.computer.hostname(www02) : can_delegate_unconstrained failed: 'NoneType' object has no attribute 'trait'     c_link.py:269

Metadata

Metadata

Assignees

Labels

bugIndicates an unexpected problem or unintended behavior

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions