Skip to content

feat(metras): add Metras stream connector for blocklist sync (#7042)#7041

Open
Khidr6G wants to merge 3 commits into
OpenCTI-Platform:masterfrom
Khidr6G:feature/metras-stream
Open

feat(metras): add Metras stream connector for blocklist sync (#7042)#7041
Khidr6G wants to merge 3 commits into
OpenCTI-Platform:masterfrom
Khidr6G:feature/metras-stream

Conversation

@Khidr6G

@Khidr6G Khidr6G commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

  • Add a new STREAM connector for the Metras EDR platform
    (api.metras.sa, X-API-KEY) under stream/metras/.
  • Consumes an OpenCTI live stream and forwards Indicators whose pattern resolves to a file
    name / path to the Metras custom blocklist:
    • create → POST /v1/custom-blocklist
    • update → PATCH /v1/custom-blocklist/{id}
    • delete → DELETE /v1/custom-blocklist/{id}
  • Blocklist entries are resolved by a deterministic name (opencti-<slug>) derived from the
    indicator name, not a volatile STIX id, so id changes on merge/re-import don't create
    duplicates.
  • Unsupported indicator types (IP, domain, hash, YARA, Sigma) are skipped and logged
    Metras exposes no create-IOC API for those; /v1/custom-blocklist accepts file paths only.
  • Robust runtime: msg.data parsed as a JSON string (json.loads(...)["data"]),
    listen_stream() with a keep-alive main loop, per-event errors are caught and logged (the
    connector never crashes the stream). Secrets typed pydantic.SecretStr.
  • Non-root python:3.12-alpine image; CIS-hardened compose; requires a user-created, activated
    OpenCTI live stream (CONNECTOR_LIVE_STREAM_ID), documented in the README.
  • Unit tests for settings, API client, converter, and orchestration; pyproject.toml coverage
    config. black / isort --profile black / flake8 --ignore=E,W clean.
  • Validated end-to-end against OpenCTI 7.260529.0 (pycti==7.260529.0,
    connectors-sdk @7.260529.0, support_version ">=7.260529.0").

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

This is a uni-directional (OpenCTI → Metras) forwarder: it creates no OpenCTI bundles and does
not use the Work API. The file-path-only limitation is an API constraint, not a design choice —
Metras has no endpoint to create IP/domain/hash/YARA/Sigma IOCs.

Copilot AI review requested due to automatic review settings July 17, 2026 18:58
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.33083% with 47 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
stream/metras/src/metras_client/api_client.py 78.81% 25 Missing ⚠️
stream/metras/src/connector/connector.py 74.32% 19 Missing ⚠️
...ream/metras/src/connector/converter_to_external.py 95.65% 2 Missing ⚠️
stream/metras/src/connector/utils.py 83.33% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           master    #7041       +/-   ##
===========================================
+ Coverage   35.64%   82.18%   +46.54%     
===========================================
  Files        2048        9     -2039     
  Lines      125310      612   -124698     
===========================================
- Hits        44672      503    -44169     
+ Misses      80638      109    -80529     
Files with missing lines Coverage Δ
stream/metras/src/connector/__init__.py 100.00% <100.00%> (ø)
stream/metras/src/connector/settings.py 100.00% <100.00%> (ø)
stream/metras/src/metras_client/__init__.py 100.00% <100.00%> (ø)
stream/metras/src/connector/utils.py 83.33% <83.33%> (ø)
...ream/metras/src/connector/converter_to_external.py 95.65% <95.65%> (ø)
stream/metras/src/connector/connector.py 74.32% <74.32%> (ø)
stream/metras/src/metras_client/api_client.py 78.81% <78.81%> (ø)

... and 2044 files with indirect coverage changes

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Adds a new OpenCTI STREAM connector under stream/metras/ that consumes a configured live stream and forwards supported file-path Indicators to the Metras EDR custom blocklist API, including create/update/delete handling and unit tests.

Changes:

  • Introduces the Metras stream connector implementation (settings, stream listener/orchestration, indicator→blocklist conversion).
  • Adds a shared Metras REST API client with retries and error handling for blocklist operations.
  • Adds connector packaging/runtime assets (Dockerfile, compose, config sample, metadata manifest) and unit tests/coverage config.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
stream/metras/VERSION Declares connector version.
stream/metras/tests/test-requirements.txt Test dependency entrypoint for the connector’s requirements + pytest.
stream/metras/tests/test_main.py Tests conversion logic (pattern→file paths, deterministic naming).
stream/metras/tests/test_connector/test_settings.py Tests config defaults and SecretStr handling.
stream/metras/tests/test_connector/test_orchestration.py Tests event processing behavior with mocked client/helper.
stream/metras/tests/test_connector/test_client.py Tests API client request/response behavior with mocked HTTP.
stream/metras/tests/conftest.py Ensures src/ is importable during tests.
stream/metras/src/requirements.txt Pins runtime dependencies (pycti, pydantic, requests, connectors-sdk).
stream/metras/src/metras_client/api_client.py Implements HTTP client for Metras API + blocklist endpoints.
stream/metras/src/metras_client/init.py Exposes MetrasClient / MetrasAPIError package API.
stream/metras/src/main.py Runtime entrypoint wiring settings → helper → connector.
stream/metras/src/connector/utils.py Adds pure utility helpers (slugify).
stream/metras/src/connector/settings.py Defines Pydantic settings models for connector + Metras config.
stream/metras/src/connector/converter_to_external.py Converts OpenCTI Indicators to Metras blocklist payloads.
stream/metras/src/connector/connector.py Stream listener + create/update/delete dispatch to Metras.
stream/metras/src/connector/init.py Exports connector public symbols for imports.
stream/metras/README.md Documents scope, configuration, and usage.
stream/metras/pyproject.toml Coverage configuration for the connector modules.
stream/metras/entrypoint.sh Container entrypoint script.
stream/metras/Dockerfile Container build for the connector runtime.
stream/metras/docker-compose.yml Example hardened compose service definition.
stream/metras/config.yml.sample Sample YAML configuration for local runs.
stream/metras/.dockerignore Docker build context pruning.
stream/metras/metadata/connector_manifest.json Connector manifest metadata for the monorepo.

paths: list[str] = []
if names and dirs:
for d in dirs:
sep = "" if d.endswith(("\\", "/")) else "\\"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed in the amended commit

Comment on lines +49 to +52
self.helper.connector_logger.info("[CONNECTOR] Starting Metras stream listener")
# listen_stream() spawns a daemon thread and returns — keep the main
# thread alive so the process does not exit.
self.helper.listen_stream(message_callback=self._process_event)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed in the amended commit

Comment thread stream/metras/src/connector/settings.py Outdated
api_base_url: HttpUrl = Field(
default="https://api.metras.sa/api",
description="Base URL of the Metras API.",
examples=["https://api.metras.sa"],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed in the amended commit

@Khidr6G Khidr6G changed the title feat(metras): add Metras stream connector for blocklist sync (#7038) feat(metras): add Metras stream connector for blocklist sync (#7042) Jul 17, 2026
@romain-filigran romain-filigran added the community Contribution from the community. label Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community Contribution from the community.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(metras): Add Metras EDR connector suite (external-import, internal-enrichment, stream)

4 participants