feat(metras): add Metras internal-enrichment connector for fleet-presence lookups (#7042)#7040
Open
Khidr6G wants to merge 3 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new Metras INTERNAL_ENRICHMENT connector under internal-enrichment/metras/ to enrich IPv4-Addr and StixFile observables with “fleet presence” context from the Metras EDR API, emitting Notes and optional System identities + related-to relationships.
Changes:
- Introduces the connector runtime (settings model, Metras API client with retries, enrichment orchestration, and STIX conversion).
- Adds containerization + deployment artifacts (Dockerfile, compose, entrypoint, sample config, metadata/manifest, versioning).
- Adds a unit test suite for settings, API client, orchestration, and utils (but no converter-specific tests despite the PR description stating otherwise).
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| internal-enrichment/metras/VERSION | Declares connector version. |
| internal-enrichment/metras/src/requirements.txt | Pins runtime dependencies (pycti, stix2, requests, connectors-sdk). |
| internal-enrichment/metras/src/main.py | Connector entrypoint wiring settings → helper → connector run loop. |
| internal-enrichment/metras/src/connector/init.py | Exposes main connector classes for imports. |
| internal-enrichment/metras/src/connector/settings.py | Pydantic/SDK-based configuration models (connector + Metras settings). |
| internal-enrichment/metras/src/connector/utils.py | Refang + basic validators used by connector/tests. |
| internal-enrichment/metras/src/connector/converter_to_stix.py | Builds deterministic STIX Note/Identity/Relationship outputs. |
| internal-enrichment/metras/src/connector/connector.py | Enrichment orchestration (scope/TLP gate, lookups, bundle send). |
| internal-enrichment/metras/src/metras_client/init.py | Exports client + error type. |
| internal-enrichment/metras/src/metras_client/api_client.py | HTTP client wrapper for Metras API endpoints + retry strategy. |
| internal-enrichment/metras/tests/test-requirements.txt | Test dependency entrypoint for isolated test runs. |
| internal-enrichment/metras/tests/conftest.py | Adds src/ to import path for tests. |
| internal-enrichment/metras/tests/test_main.py | Unit tests for utils (refang/validators). |
| internal-enrichment/metras/tests/test_connector/test_settings.py | Unit tests for settings/config model behavior. |
| internal-enrichment/metras/tests/test_connector/test_client.py | Unit tests for HTTP client behaviors (mocked session). |
| internal-enrichment/metras/tests/test_connector/test_orchestration.py | Unit tests for enrichment orchestration (mocked helper/client). |
| internal-enrichment/metras/README.md | Connector documentation (scope, behavior, configuration, usage). |
| internal-enrichment/metras/pyproject.toml | Coverage configuration for connector packages. |
| internal-enrichment/metras/entrypoint.sh | Container entrypoint script. |
| internal-enrichment/metras/Dockerfile | Container build (Alpine Python 3.12, non-root user). |
| internal-enrichment/metras/docker-compose.yml | Hardened compose example for running the connector. |
| internal-enrichment/metras/config.yml.sample | Sample YAML config for local/manual runs. |
| internal-enrichment/metras/.dockerignore | Docker build context exclusions. |
| internal-enrichment/metras/metadata/connector_manifest.json | Connector manifest metadata for OpenCTI ecosystem. |
Comment on lines
+9
to
+13
| def refang(value: str) -> str: | ||
| """Remove common defanging so values match the external API.""" | ||
| if not value: | ||
| return value | ||
| v = value |
Contributor
Author
There was a problem hiding this comment.
fixed in the amended commit
Comment on lines
+180
to
+182
| names = ", ".join( | ||
| sorted({a.get("alert_name", "?") for a in alert_data})[:10] | ||
| ) |
Contributor
Author
There was a problem hiding this comment.
fixed in the amended commit
Comment on lines
+200
to
+203
| notes.append( | ||
| f"Matches {len(endpoints)} fleet endpoint(s): " | ||
| + ", ".join(e.get("name", "?") for e in endpoints[:10]) | ||
| ) |
Contributor
Author
There was a problem hiding this comment.
fixed in the amended commit
Comment on lines
+16
to
+20
| class ConverterToStix: | ||
| def __init__(self, helper: OpenCTIConnectorHelper) -> None: | ||
| self.helper = helper | ||
| self.author = self._create_author() | ||
| self._confidence = getattr(helper, "connect_confidence_level", None) or 50 |
Contributor
Author
There was a problem hiding this comment.
fixed in the amended commit
Contributor
Author
There was a problem hiding this comment.
fixed in the amended commit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed changes
INTERNAL_ENRICHMENTconnector for the Metras EDR platform(
api.metras.sa,X-API-KEY) underinternal-enrichment/metras/.IPv4-AddrandStixFileobservables:agent_ip) + endpoint inventory (interface_ip).sha256/sha1) + binary details (md5).Notesummarizing local presence, plus an optionalIdentity(identity_class="system")for each matched fleet endpoint and arelated-torelationship. No
Sightingis emitted (stix2 forbids sighting an SCO) and noInfrastructureobjects are created.helper.check_max_tlp), refang before externalcalls,
entity_in_scopeguard withfile → stixfilealias, and a raisedValueErrorontotal lookup failure (never silently completes with 0 objects). Playbook-compatible
(
playbook_supported: true).enrichment_entity.standard_idthenstix_entity.id(never the internal DB id); deterministic IDs for notes/identities/relationships.pydantic.SecretStr; typed SDK config (TLPLevel,ListFromString);structured logging; CIS-hardened compose; non-root
python:3.12-alpineimage.pyproject.tomlcoverageconfig.
black/isort --profile black/flake8 --ignore=E,Wclean.7.260529.0(pycti==7.260529.0,connectors-sdk @7.260529.0,support_version ">=7.260529.0").Related issues
Checklist
Further comments
Scope is intentionally limited to
IPv4-AddrandStixFile: Metras has no value-filterablelookup for domains/URLs (the
/v4/threats/detailendpoint requires incident identifiers, not asingle observable value), so enriching those types is not possible with the current API.