Relations¶
In the OWASP Open Asset Model, a relation is a typed, directed connection between two assets that expresses how they are linked in the external world. Relations transform isolated observations into an interconnected graph, enabling reasoning over ownership, control, communication, service structure, and many other dimensions. Each relation carries contextual metadata such as discovery source, timestamps, and confidence, and is a first-class citizen in the data model—critical for building a coherent picture of an organization's external footprint.
Why Relations Matter¶
While assets are the atomic units of external exposure, relations are what make those atoms intelligible as systems. They represent the structure, flow, and attribution logic that connects seemingly disparate observations into a meaningful model.
Relations bring three core advantages to the OAM:
- Contextual Linking – Relations encode meaningful semantics (e.g., announces, contains, registration) between assets.
- Graph Navigation – They enable powerful queries such as tracing supply chain dependencies or resolving domain-to-IP mappings.
- Explainability – Each relation retains source, timestamp, and confidence, making inferences and automated decisions transparent and reproducible.
Relation Definition¶
Relation: A typed, directional edge connecting two assets that captures a discoverable or inferred relationship between them.
Each relation answers three questions:
- What is the connection? The label (e.g., dns_record, contains, owns, announces).
- What assets are involved? A source asset and a target asset, each uniquely identified.
- When was it discovered? A timestamp for when it was first and most recently seen.
Core Relation Schema (Conceptually)¶
{
"label": "contains",
"source": "Netblock/192.0.2.0-24",
"target": "IPAddress/192.0.2.4",
"created_at": "2025-06-20T14:22:00Z",
"last_seen": "2025-06-20T14:22:00Z",
}
See specific relation types for actual JSON field names. Also, see Triples for more information regarding how to query the data collected.
Common Relation Labels (Partial)¶
| Relation Type | From (Source Asset) | To (Target Asset) | Meaning |
|---|---|---|---|
| dns_record | FQDN | IPAddress | DNS resolved A/AAAA record |
| contains | Netblock | IPAddress | IP belongs to CIDR range |
| announces | AutonomousSystem | Netblock | AS BGP route announcement |
| registration | Netblock | IPNetRecord | Ownership or allocation data |
The list is extensible—new relation types are added as threat models and data sources evolve.
Directionality and Semantics¶
Relations are directed: a relation from A → B is not the same as B → A. For instance:
- FQDN → IPAddress via
dns_recordindicates resolution; - IPAddress → FQDN is not automatically implied and may require reverse DNS (
ptr_record).
Understanding directionality is key to constructing valid traversal paths and interpreting graph queries.
Role in Graph Queries¶
Relations are the edges that enable:
- Ownership traversal: Domain → Organization → LegalName
- Infrastructure mapping: Service → IP → Netblock → AS
- Contact pivoting: TLSCertificate → ContactRecord → Organization
The expressive power of the model arises from chaining these relations through triple-based queries.
Example:
“Which TLS certificates are served from IPs owned by ASNs linked to Acme Corp?”
This query may walk:
Organization → organization → ContactRecord → registrant → AutnumRecord → registration → AutonomousSystem → announces → Netblock → contains → IPAddress → port → Service → certificate → TLSCertificate
Where to Go Next¶
Learn more about the structure and usage of the model:
- Assets – The core entities in the graph.
- Properties – Descriptive metadata that enrich assets.
- Triples – Performing graph queries using SPARQL-style syntax.
Technical Reference¶
Relation Interface¶
The Relation interface defines the contract that all relationship types must implement:
| Method | Return Type | Purpose |
|---|---|---|
Label() |
string |
Returns the semantic relationship label (e.g., "dns_record", "port", "certificate") |
RelationType() |
RelationType |
Returns the type constant identifying the concrete implementation |
JSON() |
([]byte, error) |
Serializes the relationship data to JSON format for persistence or transmission |
The Label() identifies the semantic nature of the relationship (what it represents), while RelationType() identifies the structural implementation (how it's represented). This separation allows multiple RelationType implementations to share the same label when they have different data structures.
RelationType constants:
| RelationType Constant | Value | Use Case |
|---|---|---|
BasicDNSRelation |
"BasicDNSRelation" | A, AAAA, CNAME, NS DNS records with destination only |
PortRelation |
"PortRelation" | Network port and protocol information (port number + TCP/UDP) |
PrefDNSRelation |
"PrefDNSRelation" | MX records with preference/priority value |
SimpleRelation |
"SimpleRelation" | Generic directed connection with no additional data |
SRVDNSRelation |
"SRVDNSRelation" | SRV records with priority, weight, and port information |
Relationship Taxonomy¶
The relationship taxonomy uses a three-level nested map structure to define valid relationships:
graph TD
Root["map[string]map[RelationType][]AssetType"]
Level1["Outer Map Key: Label (string)"]
Level2["Middle Map Key: RelationType"]
Level3["Inner Slice: []AssetType"]
Root --> Level1
Level1 --> Level2
Level2 --> Level3
Example1["Example: fqdnRels['dns_record']"]
Example2["map[RelationType][]AssetType"]
Example3a["BasicDNSRelation → [FQDN, IPAddress]"]
Example3b["PrefDNSRelation → [FQDN]"]
Example3c["SRVDNSRelation → [FQDN]"]
Example1 --> Example2
Example2 --> Example3a
Example2 --> Example3b
Example2 --> Example3c
subgraph "Type Definitions"
Label["Label: Semantic name<br/>(port, dns_record, location)"]
RType["RelationType: Enumeration<br/>(BasicDNSRelation, SimpleRelation, etc.)"]
DTypes["Destination Types: Asset type constants<br/>(FQDN, IPAddress, Organization, etc.)"]
end
FQDN Relationships¶
| Label | RelationType | Destination Types | Semantic Meaning |
|---|---|---|---|
"port" |
PortRelation |
[Service] |
Services listening on FQDN |
"dns_record" |
BasicDNSRelation |
[FQDN, IPAddress] |
A, AAAA, CNAME, NS records |
"dns_record" |
PrefDNSRelation |
[FQDN] |
MX records with preference |
"dns_record" |
SRVDNSRelation |
[FQDN] |
SRV records with priority/weight |
"node" |
SimpleRelation |
[FQDN] |
Subdomain/parent relationships |
"registration" |
SimpleRelation |
[DomainRecord] |
WHOIS/RDAP registration |
IPAddress Relationships¶
| Label | RelationType | Destination Types | Semantic Meaning |
|---|---|---|---|
"port" |
PortRelation |
[Service] |
Services listening on IP |
"ptr_record" |
SimpleRelation |
[FQDN] |
Reverse DNS PTR record |
Network Infrastructure Relationships¶
The "dns_record" label is the only label in the entire taxonomy that maps to more than one RelationType:
graph LR
FQDN["FQDN Asset"]
Label["Label: 'dns_record'"]
FQDN --> Label
Label --> Basic["BasicDNSRelation"]
Label --> Pref["PrefDNSRelation"]
Label --> SRV["SRVDNSRelation"]
Basic --> BasicDest["Destinations: [FQDN, IPAddress]"]
Pref --> PrefDest["Destinations: [FQDN]"]
SRV --> SRVDest["Destinations: [FQDN]"]
BasicDest --> A["A record: FQDN → IPAddress"]
BasicDest --> AAAA["AAAA record: FQDN → IPAddress"]
BasicDest --> CNAME["CNAME record: FQDN → FQDN"]
BasicDest --> NS["NS record: FQDN → FQDN"]
PrefDest --> MX["MX record: FQDN → FQDN<br/>(includes preference value)"]
SRVDest --> SRVRecord["SRV record: FQDN → FQDN<br/>(includes priority, weight, port)"]
DNS Relationship Types¶
The three DNS-specific RelationType values each correspond to different DNS record classes:
graph TD
Relation["Relation Interface<br/>(Label, RelationType, JSON)"]
Relation --> DNS["DNS Relationship Types"]
Relation --> General["General Relationship Types"]
DNS --> BasicDNSRelation["BasicDNSRelation<br/>struct in dns/dns_record.go"]
DNS --> PrefDNSRelation["PrefDNSRelation<br/>struct in dns/dns_record.go"]
DNS --> SRVDNSRelation["SRVDNSRelation<br/>struct in dns/dns_record.go"]
BasicDNSRelation --> BasicFields["Name: string<br/>Header: RRHeader"]
PrefDNSRelation --> PrefFields["Name: string<br/>Header: RRHeader<br/>Preference: int"]
SRVDNSRelation --> SRVFields["Name: string<br/>Header: RRHeader<br/>Priority: int<br/>Weight: int<br/>Port: int"]
DNS RelationType constants:
| Constant | Purpose | Primary Use Case |
|---|---|---|
BasicDNSRelation |
Standard DNS records without ordering | A, AAAA, CNAME, NS, PTR records |
PrefDNSRelation |
DNS records with preference values | MX records |
SRVDNSRelation |
Service location records with priority/weight | SRV records |
PortRelation and SimpleRelation¶
graph TD
subgraph "SimpleRelation Implementation"
Interface["Relation Interface"]
Constant["SimpleRelation Constant"]
Struct["SimpleRelation Struct"]
end
subgraph "Struct Fields"
Field["Name string<br/>json:label"]
end
subgraph "Interface Methods"
LabelM["Label() string"]
Type["RelationType() RelationType"]
JSON["JSON() ([]byte, error)"]
end
Interface -.defines.-> Constant
Constant -.implemented by.-> Struct
Struct --> Field
Struct --> LabelM
Struct --> Type
Struct --> JSON
PortRelation appears in exactly three asset type relationship maps, always with the label "port" targeting the Service asset type:
| Source Asset Type | Label | RelationType | Destination Asset Type |
|---|---|---|---|
FQDN |
port |
PortRelation |
Service |
IPAddress |
port |
PortRelation |
Service |
URL |
port |
PortRelation |
Service |
© 2025 Jeff Foley — Licensed under Apache 2.0.