fix(misp): eliminate N+1 HTTP requests in MISP connector. Closes #3571#3579
Conversation
There was a problem hiding this comment.
Pull request overview
This PR optimizes the MISP connector to avoid an N+1 HTTP request pattern when creating events with multiple attributes, reducing connector runtime/network overhead and addressing issue #3571.
Changes:
- Pre-attaches all generated attributes to the local
pymisp.MISPEventbefore callingadd_event(), so event + attributes are sent in a single request. - Avoids recomputing the base attribute by caching it once in
run(). - Fixes hash type normalization by correctly applying
replace("-", "")when mapping hash classifications.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # bulk: attach all attributes to the event object before sending | ||
| for attr in attributes: | ||
| misp_instance.add_attribute(misp_event.id, attr) | ||
| event.add_attribute( | ||
| attr.type, | ||
| attr.value, | ||
| **{k: v for k, v in attr.to_dict().items() if k not in ("type", "value", "uuid")}, | ||
| ) | ||
|
|
||
| # add event + all attributes in a single request (bulk optimization) | ||
| misp_event = misp_instance.add_event(event, pythonify=True) | ||
|
|
||
| result = misp_instance.get_event(misp_event.id) | ||
|
|
There was a problem hiding this comment.
Optional: add a regression test for this bulk optimization (e.g., patch pymisp.PyMISP and assert add_event is called once and add_attribute is not invoked per-attribute) to help prevent the N+1 pattern from being reintroduced later.
fb35091 to
5ab8e7c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # bulk: attach all attributes to the event object before sending | ||
| for attr in attributes: | ||
| misp_instance.add_attribute(misp_event.id, attr) | ||
| event.add_attribute( | ||
| attr.type, | ||
| attr.value, | ||
| **{k: v for k, v in attr.to_dict().items() if k not in ("type", "value", "uuid")}, | ||
| ) | ||
|
|
||
| # single request — event + all attributes sent together | ||
| misp_event = misp_instance.add_event(event, pythonify=True) |
There was a problem hiding this comment.
The refactor changes the connector’s behavior from multiple PyMISP.add_attribute() calls to a single add_event() payload. There are unit tests for other connectors under tests/api_app/connectors_manager/, but none covering the MISP connector’s request pattern. Please add a test that mocks pymisp.PyMISP and asserts add_event() is called once and add_attribute() is not called, so the N+1 regression is prevented.
4eb3f22 to
34da343
Compare
mlodic
left a comment
There was a problem hiding this comment.
without unittests this won't be merged. I also need proof that this work with a MISP instance otherwise this is all theoretical only
okay. Understood. I will update this PR shortly |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # bulk: attach all attributes to the event object before sending | ||
| for attr in attributes: | ||
| misp_instance.add_attribute(misp_event.id, attr) | ||
| event.add_attribute( | ||
| attr.type, | ||
| attr.value, |
There was a problem hiding this comment.
This change materially alters how attributes are sent to MISP (bundled into the initial add_event call). There don’t appear to be unit tests covering the MISP connector; please add tests to assert that PyMISP.add_event is called once with the event containing all expected attributes and that PyMISP.add_attribute is no longer called (preventing regressions back to N+1 requests).
| # append attribute name to event info | ||
| event.info += f": {self._base_attr_obj.value}" | ||
|
|
||
| # add event to MISP Instance | ||
| misp_event = misp_instance.add_event(event, pythonify=True) | ||
| # add attributes to event on MISP Instance | ||
| # bulk: attach all attributes to the event object before sending |
There was a problem hiding this comment.
event.info += f": {self._base_attr_obj.value}" recomputes _base_attr_obj even though it was already computed when building attributes above. Since _base_attr_obj performs a DB query to build the analyzers list, consider computing it once (e.g., base_attr = self._base_attr_obj) and reusing it for both event.info and the attributes list to avoid duplicate queries and keep values consistent.
a374c35 to
416b018
Compare
416b018 to
8f8b906
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| from api_app.analyzables_manager.models import Analyzable | ||
| from api_app.choices import Classification | ||
| from api_app.connectors_manager.connectors.misp import MISP | ||
| from api_app.connectors_manager.models import ConnectorConfig, ConnectorReport | ||
| from api_app.models import Job, Parameter, PluginConfig | ||
| from tests import CustomTestCase |
There was a problem hiding this comment.
PluginConfig is imported but never used in this test module. Removing unused imports avoids lint noise and keeps the test focused.
8f8b906 to
d74da6f
Compare
|
Hi @mlodic, sorry for the delay, was busy in college work.As asked I implemented unittests, and attached proof that this works with MISP Instance, can you review them, when you get a chance. |






Description
The
MISP.run()method calledmisp_instance.add_attribute()in a loopafter
add_event(), issuing one HTTP request per attribute (N+1 pattern).Fixed by pre-attaching all attributes to the local
MISPEventobjectbefore calling
add_event(), so MISP persists the event and all itsattributes in a single request.
Closes #3571
Type of change
Checklist
developRuff) gave 0 errors.