Skip to content

fix(recorded-future): include context_entities so their threat actors aren't dropped (#6943) - #7706

Open
Hugo Dupras (jabesq) wants to merge 1 commit into
masterfrom
fix/6943-recorded-future-context-entities
Open

Hugo Dupras (jabesq) wants to merge 1 commit into
masterfrom
fix/6943-recorded-future-context-entities

Conversation

@jabesq

Copy link
Copy Markdown
Member

Proposed changes

  • Process context_entities alongside note_entities in StixNote.from_json() (external-import/recorded-future/src/rflib/rf_to_stix2.py). Both arrays now flow through the exact same per-entity conversion logic (type mapping, self.tas lookup, person_to_ta, ta_to_intrusion_set, risk-score/threshold handling), so an entity that Recorded Future only tags as contextual is no longer silently dropped.
  • Deduplicate the merged entities by their Recorded Future entity id, so an entity present in both arrays yields a single STIX object and a single object_refs entry on the report.
  • Add two regression tests in tests/tests_connector/test_rf_to_stix2.py:
    • test_analyst_note_includes_context_entities — a Threat Actor present only in context_entities is converted and referenced by the report.
    • test_analyst_note_deduplicates_entities_shared_between_both_arrays — an entity id present in both arrays is referenced only once.

Related issues

Checklist

  • I consider the submitted work as finished
  • I have signed my commits using GPG key.
  • I tested the code for its functionality using different use cases
  • I added/update the relevant documentation (either on github or on notion)
  • Where necessary I refactored code to improve the overall quality

Further comments

Root cause — Recorded Future Analyst Notes carry two entity arrays on attributes: note_entities (the primary entities the note is directly about) and context_entities (related/referenced entities discussed in the note's narrative, which can include threat actors, but are not tagged as primary). StixNote.from_json() only ever iterated attr.get("note_entities", []). When Recorded Future classified a threat actor named in a report's narrative as a context entity rather than a primary one, the report was still created referencing that actor in its text, but no corresponding STIX Threat Actor / Intrusion Set object (or relationship to it) ever reached the bundle sent to OpenCTI — leaving an incomplete knowledge graph for that report.

Validation — the full connector suite passes (84 tests, Python 3.12), along with isort, black, flake8 --ignore=E,W and the custom STIX deterministic-ID pylint plugin (no no_generated_id_stix findings). A revert-check was performed: reverting only the from_json() loop change while keeping the new tests makes test_analyst_note_includes_context_entities fail with assert 1 == 2 (the context-only Threat Actor missing from report.object_refs), confirming the regression test genuinely catches the bug.

Attribution — this re-submits the work from #7466 (closed) authored by agu2347; the original commit authorship is preserved.

… aren't dropped (#6943)

Fixes #6943

Process note_entities and context_entities together through the same
existing per-entity conversion logic, deduplicating by entity id.

Signed-off-by: agu2347 <agu2347@users.noreply.github.com>
@jabesq Hugo Dupras (jabesq) added the community Contribution from the community. label Sep 16, 2026
@jabesq
Hugo Dupras (jabesq) marked this pull request as ready for review September 16, 2026 14:34
Copilot AI lite review requested due to automatic review settings September 16, 2026 14:34
@github-actions github-actions Bot added the filigran team Item from the Filigran team. label Sep 16, 2026
@github-actions

Copy link
Copy Markdown

⚠️ Unused dependencies detected

The following packages appear to be unused:

  • antlr4-python3-runtime in external-import/recorded-future/src/requirements.txt
  • chardet in external-import/recorded-future/src/requirements.txt
  • datefinder in external-import/recorded-future/src/requirements.txt
  • idna in external-import/recorded-future/src/requirements.txt
  • pika in external-import/recorded-future/src/requirements.txt
  • python-dateutil in external-import/recorded-future/src/requirements.txt
  • PyYAML in external-import/recorded-future/src/requirements.txt
  • regex in external-import/recorded-future/src/requirements.txt
  • rfapi in external-import/recorded-future/src/requirements.txt
  • simplejson in external-import/recorded-future/src/requirements.txt
  • six in external-import/recorded-future/src/requirements.txt
  • sseclient in external-import/recorded-future/src/requirements.txt
  • stix2-patterns in external-import/recorded-future/src/requirements.txt
  • urllib3 in external-import/recorded-future/src/requirements.txt

How to fix: (1) if the package is used but under a different import name (e.g. PyYAMLyaml), add a mapping line to .github/deptry-package-map.txt. (2) if truly unused, remove it from the connector's requirements.txt.

@github-actions

Copy link
Copy Markdown

🔴 Connector Linter errors detected

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR updates Recorded Future Analyst Note ingestion to retain contextual entities and deduplicate shared entities.

Changes:

  • Processes note_entities and context_entities through shared conversion logic.
  • Deduplicates merged entities by Recorded Future ID.
  • Adds regression tests for contextual entities and duplicates.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Summary
external-import/recorded-future/src/rflib/rf_to_stix2.py Includes and deduplicates both entity arrays.
external-import/recorded-future/tests/tests_connector/test_rf_to_stix2.py Adds regression coverage, but the deduplication assertion does not prove both arrays were processed or that only one STIX entity was produced.
Suppressed comments (1)

external-import/recorded-future/tests/tests_connector/test_rf_to_stix2.py:404

  • This assertion does not actually verify the new deduplication path: StixNote.to_stix_objects() already applies dict.fromkeys when building report.object_refs, so it remains 1 even if the context entity is processed a second time (and it also passes with the old implementation that ignores context_entities). Please adjust the fixture/assertions to prove both arrays were processed and that only one STIX entity object was produced, for example by adding another context-only entity and checking the resulting object count or by spying on the conversion call.
    assert len(report.object_refs) == 1

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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

Labels

community Contribution from the community. filigran team Item from the Filigran team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(recorded-future): threat actors listed in context_entities are missing from analyst note STIX bundle

4 participants