CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-stix2

Produce and consume STIX 2 JSON content for cyber threat intelligence

Overview
Eval results
Files

relationships.mddocs/

Relationships and Object Links

STIX Relationship Objects and utilities for creating and managing connections between STIX objects, including relationships, sightings, and reference resolution across multiple data sources.

Capabilities

Relationship Objects

Connect STIX Domain Objects with typed relationships describing how they relate to each other.

class Relationship:
    """
    Relationship objects describe connections between STIX Domain Objects.
    
    Required Properties:
    - relationship_type (str): Type of relationship
    - source_ref (str): ID of source STIX object
    - target_ref (str): ID of target STIX object
    
    Optional Properties:
    - description (str): Description of the relationship
    - start_time (timestamp): When relationship started
    - stop_time (timestamp): When relationship ended
    """

Usage examples:

from stix2 import Relationship, ThreatActor, Malware, AttackPattern

# Create objects to relate
threat_actor = ThreatActor(
    name="APT1",
    threat_actor_types=["nation-state"]
)

malware = Malware(
    name="Poison Ivy",
    malware_types=["remote-access-trojan"]
)

attack_pattern = AttackPattern(
    name="Spear Phishing"
)

# Create relationships
uses_malware = Relationship(
    relationship_type="uses",
    source_ref=threat_actor.id,
    target_ref=malware.id,
    description="APT1 commonly uses Poison Ivy RAT in their operations"
)

uses_technique = Relationship(
    relationship_type="uses", 
    source_ref=threat_actor.id,
    target_ref=attack_pattern.id,
    description="APT1 uses spear phishing for initial access"
)

implements_pattern = Relationship(
    relationship_type="implements",
    source_ref=malware.id,
    target_ref=attack_pattern.id,
    description="Poison Ivy is delivered via spear phishing attacks"
)

# Time-bounded relationship
active_relationship = Relationship(
    relationship_type="targets",
    source_ref=threat_actor.id,
    target_ref="identity--target-organization-uuid",
    start_time="2020-01-01T00:00:00.000Z",
    stop_time="2020-12-31T23:59:59.000Z",
    description="APT1 actively targeted this organization during 2020"
)

Common Relationship Types

STIX defines standard relationship types for connecting different object types:

# Threat Actor relationships
actor_uses_malware = Relationship(
    relationship_type="uses",           # ThreatActor -> Malware
    source_ref=threat_actor.id,
    target_ref=malware.id
)

actor_targets_identity = Relationship(
    relationship_type="targets",        # ThreatActor -> Identity
    source_ref=threat_actor.id, 
    target_ref=identity.id
)

actor_attributed_to = Relationship(
    relationship_type="attributed-to",  # ThreatActor -> Identity
    source_ref=threat_actor.id,
    target_ref=sponsor_identity.id
)

actor_impersonates = Relationship(
    relationship_type="impersonates",   # ThreatActor -> Identity
    source_ref=threat_actor.id,
    target_ref=impersonated_identity.id
)

# Malware relationships  
malware_targets_identity = Relationship(
    relationship_type="targets",        # Malware -> Identity
    source_ref=malware.id,
    target_ref=victim_identity.id
)

malware_uses_tool = Relationship(
    relationship_type="uses",           # Malware -> Tool
    source_ref=malware.id,
    target_ref=tool.id
)

malware_variant_of = Relationship(
    relationship_type="variant-of",     # Malware -> Malware
    source_ref=malware_variant.id,
    target_ref=malware_family.id
)

# Indicator relationships
indicator_indicates = Relationship(
    relationship_type="indicates",      # Indicator -> ThreatActor/Malware/AttackPattern
    source_ref=indicator.id,
    target_ref=malware.id
)

# Campaign relationships
campaign_uses_malware = Relationship(
    relationship_type="uses",           # Campaign -> Malware
    source_ref=campaign.id,
    target_ref=malware.id
)

campaign_attributed_to = Relationship(
    relationship_type="attributed-to",  # Campaign -> ThreatActor
    source_ref=campaign.id,
    target_ref=threat_actor.id
)

# Attack Pattern relationships
pattern_mitigated_by = Relationship(
    relationship_type="mitigated-by",   # AttackPattern -> CourseOfAction
    source_ref=attack_pattern.id,
    target_ref=course_of_action.id
)

# Course of Action relationships
coa_mitigates = Relationship(
    relationship_type="mitigates",      # CourseOfAction -> AttackPattern/Malware
    source_ref=course_of_action.id,
    target_ref=attack_pattern.id
)

# Vulnerability relationships
vuln_targets = Relationship(
    relationship_type="targets",        # Vulnerability -> Identity/Software
    source_ref=vulnerability.id,
    target_ref=software.id
)

# Tool relationships
tool_targets = Relationship(
    relationship_type="targets",        # Tool -> Identity/Infrastructure
    source_ref=tool.id, 
    target_ref=infrastructure.id
)

# Infrastructure relationships
infra_communicates_with = Relationship(
    relationship_type="communicates-with",  # Infrastructure -> Infrastructure
    source_ref=infrastructure1.id,
    target_ref=infrastructure2.id
)

infra_consists_of = Relationship(
    relationship_type="consists-of",    # Infrastructure -> Infrastructure/Software
    source_ref=infrastructure.id,
    target_ref=component.id
)

infra_controls = Relationship(
    relationship_type="controls",       # Infrastructure -> Infrastructure/Malware
    source_ref=c2_server.id,
    target_ref=malware.id
)

infra_delivers = Relationship(
    relationship_type="delivers",       # Infrastructure -> Malware
    source_ref=infrastructure.id,
    target_ref=malware.id
)

infra_hosts = Relationship(
    relationship_type="hosts",          # Infrastructure -> Tool/Malware
    source_ref=infrastructure.id,
    target_ref=malware.id
)

infra_owns = Relationship(
    relationship_type="owns",           # Infrastructure -> Infrastructure
    source_ref=infrastructure.id,
    target_ref=owned_infrastructure.id
)

# Report relationships
report_object_refs = [                  # Report -> any SDO/SRO (via object_refs property)
    threat_actor.id,
    malware.id,
    indicator.id,
    uses_malware.id
]

report = Report(
    name="APT1 Analysis Report",
    published="2021-04-23T10:30:00.000Z",
    object_refs=report_object_refs
)

Sighting Objects

Represent observations or encounters with STIX objects in the real world.

class Sighting:
    """
    Sighting objects represent observations of STIX objects.
    
    Required Properties:
    - sighting_of_ref (str): ID of sighted STIX object
    
    Optional Properties:
    - count (int): Number of times sighted
    - first_seen (timestamp): First sighting time
    - last_seen (timestamp): Last sighting time
    - where_sighted_refs (list): References to identities that sighted
    - observed_data_refs (list): References to observed data
    - summary (bool): Whether this is a summary sighting
    - description (str): Description of the sighting
    """

Usage examples:

from stix2 import Sighting, Indicator, Identity

# Create indicator and observer identity
indicator = Indicator(
    name="Malicious Domain",
    indicator_types=["malicious-activity"],
    pattern_type="stix",
    pattern="[domain-name:value = 'evil.com']"
)

security_org = Identity(
    name="ACME Security Team",
    identity_class="organization"
)

# Create sighting of indicator
sighting = Sighting(
    sighting_of_ref=indicator.id,
    count=5,
    first_seen="2021-04-23T10:30:00.000Z",
    last_seen="2021-04-23T11:45:00.000Z",
    where_sighted_refs=[security_org.id],
    description="Domain observed in network traffic attempting C2 communication"
)

# Sighting with observed data
from stix2 import ObservedData

observed_data = ObservedData(
    first_observed="2021-04-23T10:30:00.000Z",
    last_observed="2021-04-23T10:30:00.000Z",
    number_observed=1,
    objects={
        "0": {
            "type": "domain-name",
            "value": "evil.com"
        }
    }
)

detailed_sighting = Sighting(
    sighting_of_ref=indicator.id,
    count=1,
    first_seen="2021-04-23T10:30:00.000Z",
    where_sighted_refs=[security_org.id],
    observed_data_refs=[observed_data.id],
    description="Domain observed in DNS logs"
)

# Summary sighting (aggregated data)
summary_sighting = Sighting(
    sighting_of_ref=indicator.id,
    count=127,
    first_seen="2021-04-01T00:00:00.000Z",
    last_seen="2021-04-30T23:59:59.000Z",
    where_sighted_refs=[security_org.id],
    summary=True,
    description="Monthly summary: domain seen 127 times across network"
)

Reference Resolution

Utilities for resolving object references across data sources.

from stix2 import MemoryStore, CompositeDataSource

# Store related objects
store = MemoryStore([
    threat_actor,
    malware,
    uses_malware,
    indicator,
    sighting
])

# Resolve relationship targets
relationship = store.get(uses_malware.id)
source_object = store.get(relationship.source_ref)  # ThreatActor
target_object = store.get(relationship.target_ref)  # Malware

print(f"{source_object.name} uses {target_object.name}")

# Find all relationships involving an object
threat_actor_relationships = store.query([
    Filter('type', '=', 'relationship'),
    Filter('source_ref', '=', threat_actor.id)
])

# Find sightings of an indicator
indicator_sightings = store.query([
    Filter('type', '=', 'sighting'),
    Filter('sighting_of_ref', '=', indicator.id)
])

# Composite data source for cross-store resolution
composite = CompositeDataSource([
    memory_store,
    filesystem_store,
    taxii_store
])

# Resolve references across multiple stores
resolved_relationship = composite.get(relationship_id)
resolved_source = composite.get(resolved_relationship.source_ref)

Relationship Patterns

Common patterns for modeling threat intelligence relationships:

# Attribution chain: Campaign -> ThreatActor -> Identity
campaign_attribution = Relationship(
    relationship_type="attributed-to",
    source_ref=campaign.id,
    target_ref=threat_actor.id
)

actor_attribution = Relationship(
    relationship_type="attributed-to", 
    source_ref=threat_actor.id,
    target_ref=nation_state_identity.id
)

# Attack chain: ThreatActor -> AttackPattern -> Vulnerability -> Software
actor_uses_pattern = Relationship(
    relationship_type="uses",
    source_ref=threat_actor.id,
    target_ref=attack_pattern.id
)

pattern_targets_vuln = Relationship(
    relationship_type="targets",
    source_ref=attack_pattern.id,
    target_ref=vulnerability.id
)

vuln_in_software = Relationship(
    relationship_type="targets",
    source_ref=vulnerability.id,
    target_ref=software.id
)

# Infrastructure relationships: C2 -> Malware <- ThreatActor
c2_hosts_malware = Relationship(
    relationship_type="hosts",
    source_ref=c2_infrastructure.id,
    target_ref=malware.id
)

actor_uses_c2 = Relationship(
    relationship_type="uses",
    source_ref=threat_actor.id,
    target_ref=c2_infrastructure.id
)

# Mitigation relationships: CourseOfAction -> AttackPattern/Malware
mitigation_blocks_pattern = Relationship(
    relationship_type="mitigates",
    source_ref=course_of_action.id,
    target_ref=attack_pattern.id
)

# Indicator relationships: Indicator -> ThreatActor/Malware/Campaign
indicator_indicates_actor = Relationship(
    relationship_type="indicates",
    source_ref=indicator.id,
    target_ref=threat_actor.id
)

# Version relationships: Malware -> Malware (variant-of)
new_version_relationship = Relationship(
    relationship_type="variant-of",
    source_ref=malware_v2.id,
    target_ref=malware_v1.id,
    description="Version 2 of the malware family"
)

Querying Relationships

Find and analyze relationships between objects:

# Find all objects a threat actor uses
threat_actor_uses = store.query([
    Filter('type', '=', 'relationship'),
    Filter('relationship_type', '=', 'uses'),
    Filter('source_ref', '=', threat_actor.id)
])

for rel in threat_actor_uses:
    target = store.get(rel.target_ref)
    print(f"ThreatActor uses {target.type}: {target.name}")

# Find all indicators that indicate a specific malware
malware_indicators = store.query([
    Filter('type', '=', 'relationship'),
    Filter('relationship_type', '=', 'indicates'),
    Filter('target_ref', '=', malware.id)
])

for rel in malware_indicators:
    indicator = store.get(rel.source_ref)
    print(f"Indicator: {indicator.name}")

# Find all sightings by a specific organization
org_sightings = store.query([
    Filter('type', '=', 'sighting'),
    Filter('where_sighted_refs', 'contains', security_org.id)
])

# Complex relationship queries
# Find all malware used by threat actors targeting a specific sector
financial_targets = store.query([
    Filter('type', '=', 'identity'),
    Filter('sectors', 'contains', 'financial-services')
])

for target in financial_targets:
    # Find threat actors targeting this identity
    targeting_rels = store.query([
        Filter('type', '=', 'relationship'),
        Filter('relationship_type', '=', 'targets'),  
        Filter('target_ref', '=', target.id)
    ])
    
    for targeting_rel in targeting_rels:
        actor = store.get(targeting_rel.source_ref)
        if actor and actor.type == 'threat-actor':
            # Find malware used by this actor
            malware_rels = store.query([
                Filter('type', '=', 'relationship'),
                Filter('relationship_type', '=', 'uses'),
                Filter('source_ref', '=', actor.id)
            ])
            
            for malware_rel in malware_rels:
                malware = store.get(malware_rel.target_ref)
                if malware and malware.type == 'malware':
                    print(f"{actor.name} uses {malware.name} against {target.name}")

Install with Tessl CLI

npx tessl i tessl/pypi-stix2

docs

data-storage.md

equivalence.md

index.md

markings.md

object-creation.md

pattern-matching.md

relationships.md

stix-domain-objects.md

stix-observables.md

utilities.md

versioning.md

tile.json