Skip to content

feat: document multi-property matching for OpenGraph Ingest - BED-7451#223

Open
zinic wants to merge 1 commit intomainfrom
BED-7451
Open

feat: document multi-property matching for OpenGraph Ingest - BED-7451#223
zinic wants to merge 1 commit intomainfrom
BED-7451

Conversation

@zinic
Copy link
Contributor

@zinic zinic commented Mar 12, 2026

Summary by CodeRabbit

  • Documentation
    • Reworked OpenGraph schemas to use reusable definitions for node and edge properties.
    • Added property-based endpoint matching for edges alongside identifier-based matching.
    • Introduced conditional validation requiring either identifier or property matchers.
    • Expanded property value support (primitives and primitive arrays) and disallowed specific reserved keys.
    • Updated examples and naming to reflect new validation rules and snake_case conventions.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

Walkthrough

Reworked OpenGraph JSON schemas to centralize reusable definitions under $defs (property_map, endpoint), converted inline start/end and properties to $ref references, added property-based endpoint matching with conditional if/then/else validation, and updated documentation and examples to the new schema shape and snake_case naming.

Changes

Cohort / File(s) Summary
Edge schema changes
docs/assets/opengraph/opengraph-edge.json
Introduced $defs.endpoint and $defs.property_map; start/end now $ref to endpoint; properties $ref to property_map; added match_by enum (includes property), property_matchers, operator constraints, and top-level if/then/else to enforce mutually exclusive matching modes. Examples updated to property-based and identifier-based matching.
Node schema changes
docs/assets/opengraph/opengraph-node.json
Moved node properties into $defs.property_map and referenced it from root; changed property value schema to anyOf (primitives or homogeneous primitive arrays); added not constraint disallowing objectid; adjusted kinds minItems and examples (snake_case, numeric formatting).
Documentation / guide updates
docs/opengraph/schema.mdx
Expanded docs to describe new $defs, endpoint matching modes, conditional validation, updated examples and minimal payloads, and reformatted image tags and examples to match schema changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through schemas, neat and spry,
Moved traits to $defs, gave old inline a goodbye.
Match by property or by id I cheer,
Examples updated — carrots for the reviewer near! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main focus of the PR: documenting multi-property matching for OpenGraph Ingest, which is reflected across all three modified documentation files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BED-7451
📝 Coding Plan for PR comments
  • Generate coding plan

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@zinic
Copy link
Contributor Author

zinic commented Mar 12, 2026

Reopened this to stage for the next release.

@jeff-matthews
Copy link
Contributor

Hi @zinic,

I think I've got you covered in #220. Apologies for not catching this as release work in your original PR (#217).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/opengraph/schema.mdx (1)

601-620: ⚠️ Potential issue | 🟠 Major

Inconsistency: Minimal example includes forbidden objectid in properties.

The "Reserved Property: objectid" section (lines 69-76) explicitly states that objectid must NOT be included in the properties object. However, this minimal working example includes "objectid": "123" and "objectid": "234" in the properties of both nodes.

This example will fail validation against the new node schema which includes not: { required: ["objectid"] }.

🐛 Proposed fix to remove objectid from properties
       {
         "id": "123",
         "kinds": ["Person"],
         "properties": {
           "displayname": "bob",
           "property": "a",
-          "objectid": "123",
           "name": "BOB"
         }
       },
       {
         "id": "234",
         "kinds": ["Person"],
         "properties": {
           "displayname": "alice",
           "property": "b",
-          "objectid": "234",
           "name": "ALICE"
         }
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/opengraph/schema.mdx` around lines 601 - 620, The example node objects
including id "123" and "234" currently place a reserved field "objectid" inside
their properties, which violates the node schema's rule (not: { required:
["objectid"] }); remove the "objectid" entries from the properties for both
nodes in the minimal example so each properties object only contains allowed
keys like "displayname", "property", and "name", leaving the top-level "id" to
carry the node identifier.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/assets/opengraph/opengraph-node.json`:
- Around line 40-42: The schema's "kinds" property is inconsistent: it has
"minItems": 0 but the description refers to "the first element" as the primary
kind; update the constraint so the schema and description match by setting
"minItems" to 1 for the "kinds" array in opengraph-node.json (or alternatively
change the description to allow empty arrays) and ensure the "kinds" property
and its description remain consistent with examples and any validation logic
that relies on a primary element.

---

Outside diff comments:
In `@docs/opengraph/schema.mdx`:
- Around line 601-620: The example node objects including id "123" and "234"
currently place a reserved field "objectid" inside their properties, which
violates the node schema's rule (not: { required: ["objectid"] }); remove the
"objectid" entries from the properties for both nodes in the minimal example so
each properties object only contains allowed keys like "displayname",
"property", and "name", leaving the top-level "id" to carry the node identifier.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c402a873-8490-45e5-9ce7-715a8a5482f3

📥 Commits

Reviewing files that changed from the base of the PR and between cbd039f and 3278ceb.

📒 Files selected for processing (3)
  • docs/assets/opengraph/opengraph-edge.json
  • docs/assets/opengraph/opengraph-node.json
  • docs/opengraph/schema.mdx

Comment on lines +40 to 42
"minItems": 0,
"maxItems": 3,
"minItems": 1,
"description": "An array of kind labels for the node. The first element is treated as the node's primary kind and is used to determine which icon to display in the graph UI. This primary kind is only used for visual representation and has no semantic significance for data processing."
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the actual BloodHound validation code enforces minItems differently
rg -n "minItems|kinds" --type json -g 'opengraph*.json'

Repository: SpecterOps/bloodhound-docs

Length of output: 738


Inconsistent constraint: minItems: 0 contradicts the description's reference to "the first element".

Line 40 specifies minItems: 0, allowing empty kinds arrays, but line 42's description states "The first element is treated as the node's primary kind," which implies a non-empty array. All provided examples contain at least one kind.

Either update the description to reflect that empty arrays are valid, or change minItems to 1 to enforce the expected behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/assets/opengraph/opengraph-node.json` around lines 40 - 42, The schema's
"kinds" property is inconsistent: it has "minItems": 0 but the description
refers to "the first element" as the primary kind; update the constraint so the
schema and description match by setting "minItems" to 1 for the "kinds" array in
opengraph-node.json (or alternatively change the description to allow empty
arrays) and ensure the "kinds" property and its description remain consistent
with examples and any validation logic that relies on a primary element.

#217)

* feat: document multi-property matching for OpenGraph Ingest - BED-7451

* Apply suggestions from code review from @docs

Co-authored-by: Jeff Matthews <jmatthews@specterops.io>

---------

Co-authored-by: Jeff Matthews <jmatthews@specterops.io>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
docs/opengraph/schema.mdx (1)

607-626: ⚠️ Potential issue | 🟡 Minor

Minimal payload example includes objectid in properties, violating the schema.

The example nodes include "objectid": "123" and "objectid": "234" in their properties objects (lines 613, 623). However, the schema at lines 106-108 explicitly disallows objectid via the "not": { "required": ["objectid"] } constraint, and the documentation at lines 69-76 states that objectid must not be included in properties.

This example would fail validation against the documented schema. Remove the objectid entries from the properties.

🛠️ Proposed fix
       {
         "id": "123",
         "kinds": ["Person"],
         "properties": {
           "displayname": "bob",
           "property": "a",
-          "objectid": "123",
           "name": "BOB"
         }
       },
       {
         "id": "234",
         "kinds": ["Person"],
         "properties": {
           "displayname": "alice",
           "property": "b",
-          "objectid": "234",
           "name": "ALICE"
         }
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/opengraph/schema.mdx` around lines 607 - 626, The example nodes include
"objectid" inside each node's properties which violates the schema's "not": {
"required": ["objectid"] } constraint; edit the example JSON objects (the nodes
with "id": "123" and "id": "234", under their "properties" objects) and remove
the "objectid" fields so only allowed properties like "displayname", "property",
and "name" remain.
docs/assets/opengraph/opengraph-edge.json (1)

125-191: ⚠️ Potential issue | 🟡 Minor

Edge kind naming in examples uses snake_case, contradicting PascalCase recommendation.

The documentation at line 157 in schema.mdx states: "We recommend using PascalCase (for example, AdminTo, HasSession) for readability and consistency."

However, the examples here use snake_case: has_session, accessed_resource, admin_to, connected_to. While both are valid per the regex pattern, this creates inconsistency between the guidance and examples.

Consider either updating the examples to use PascalCase (HasSession, AccessedResource, AdminTo, ConnectedTo) or clarifying that both conventions are acceptable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/assets/opengraph/opengraph-edge.json` around lines 125 - 191, The
examples show edge kinds in snake_case (has_session, accessed_resource,
admin_to, connected_to) which conflicts with the docs' recommendation to use
PascalCase (AdminTo, HasSession); update the example entries that set "kind" to
use PascalCase (e.g., change "has_session" → "HasSession", "accessed_resource" →
"AccessedResource", "admin_to" → "AdminTo", "connected_to" → "ConnectedTo") or
alternatively add a brief comment clarifying both snake_case and PascalCase are
allowed to resolve the inconsistency.
🧹 Nitpick comments (1)
docs/assets/opengraph/opengraph-edge.json (1)

63-95: Conditional validation logic is correct but could be simplified.

The if/then/else structure correctly enforces mutual exclusivity between value and property_matchers based on the match_by mode. However, the allOf with the "not": { "type": "null" } check at lines 73-81 appears redundant since match_by is defined as "type": "string" and cannot be null.

This is a minor observation and doesn't affect correctness—the schema will validate correctly as-is.

♻️ Optional simplification
         "if": {
-            "allOf": [
-                {
-                    "properties": {
-                        "match_by": {
-                            "type": "string",
-                            "const": "property"
-                        }
-                    }
-                },
-                {
-                    "not": {
-                        "properties": {
-                            "match_by": {
-                                "type": "null"
-                            }
-                        }
-                    }
+            "properties": {
+                "match_by": {
+                    "const": "property"
                 }
-            ]
+            }
         },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/assets/opengraph/opengraph-edge.json` around lines 63 - 95, The
conditional schema uses an unnecessary allOf clause: remove the redundant allOf
entry that wraps a "not" check asserting match_by is not null (the check against
{"type":"null"}) and replace the if with a single properties condition that
checks "match_by" is a string const "property"; keep the then/else branches
requiring "property_matchers" vs "value" and the corresponding "not" clauses
intact so mutual exclusivity between "property_matchers" and "value" is
preserved (refer to the if/then/else block and the "match_by",
"property_matchers", and "value" fields).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@docs/assets/opengraph/opengraph-edge.json`:
- Around line 125-191: The examples show edge kinds in snake_case (has_session,
accessed_resource, admin_to, connected_to) which conflicts with the docs'
recommendation to use PascalCase (AdminTo, HasSession); update the example
entries that set "kind" to use PascalCase (e.g., change "has_session" →
"HasSession", "accessed_resource" → "AccessedResource", "admin_to" → "AdminTo",
"connected_to" → "ConnectedTo") or alternatively add a brief comment clarifying
both snake_case and PascalCase are allowed to resolve the inconsistency.

In `@docs/opengraph/schema.mdx`:
- Around line 607-626: The example nodes include "objectid" inside each node's
properties which violates the schema's "not": { "required": ["objectid"] }
constraint; edit the example JSON objects (the nodes with "id": "123" and "id":
"234", under their "properties" objects) and remove the "objectid" fields so
only allowed properties like "displayname", "property", and "name" remain.

---

Nitpick comments:
In `@docs/assets/opengraph/opengraph-edge.json`:
- Around line 63-95: The conditional schema uses an unnecessary allOf clause:
remove the redundant allOf entry that wraps a "not" check asserting match_by is
not null (the check against {"type":"null"}) and replace the if with a single
properties condition that checks "match_by" is a string const "property"; keep
the then/else branches requiring "property_matchers" vs "value" and the
corresponding "not" clauses intact so mutual exclusivity between
"property_matchers" and "value" is preserved (refer to the if/then/else block
and the "match_by", "property_matchers", and "value" fields).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 81ddfed7-36ef-4166-acd4-76366b2647ea

📥 Commits

Reviewing files that changed from the base of the PR and between 3278ceb and 4e9e1e1.

📒 Files selected for processing (3)
  • docs/assets/opengraph/opengraph-edge.json
  • docs/assets/opengraph/opengraph-node.json
  • docs/opengraph/schema.mdx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants