feat(corelight-investigator): create external-import connector for alerts and detections (#6736)#6737
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new external-import connector under external-import/corelight-investigator/ to pull Corelight Investigator “Detections and Alerts” incrementally, convert them into STIX 2.1 Incidents plus related IP observables, and send bundles to OpenCTI with stateful scheduling and configurable TLP.
Changes:
- Implemented a Corelight Investigator HTTP client with basic retry/backoff and response-envelope normalization.
- Implemented STIX conversion + connector runtime (stateful incremental
since, bundling, scheduling). - Added full connector packaging (Docker, compose, sample config, metadata) and unit tests for client/settings/converter/connector.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| external-import/corelight-investigator/src/corelight_investigator_client/api_client.py | Requests-based API client with retries/backoff and payload extraction. |
| external-import/corelight-investigator/src/corelight_investigator_client/init.py | Exposes client + error types as a small import surface. |
| external-import/corelight-investigator/src/connector/converter_to_stix.py | Alert/detection → STIX Incident + IP observables + relationships conversion. |
| external-import/corelight-investigator/src/connector/connector.py | Connector orchestration: stateful “since”, bundling, send, work handling, scheduling. |
| external-import/corelight-investigator/src/connector/settings.py | Pydantic/connectors-sdk settings models for OpenCTI + connector + Corelight config. |
| external-import/corelight-investigator/src/connector/init.py | Exposes connector entry classes. |
| external-import/corelight-investigator/src/main.py | Runtime entrypoint wiring settings → helper → connector. |
| external-import/corelight-investigator/src/requirements.txt | Connector runtime dependencies. |
| external-import/corelight-investigator/tests/conftest.py | Pytest fixtures/mocking for OpenCTIConnectorHelper. |
| external-import/corelight-investigator/tests/test_main.py | Smoke tests for settings/helper/connector instantiation. |
| external-import/corelight-investigator/tests/test-requirements.txt | Test dependency pinning for this connector. |
| external-import/corelight-investigator/tests/tests_corelight_investigator_client/test_api_client.py | Unit tests for API client behavior (envelope parsing, params, errors, rate-limit retry). |
| external-import/corelight-investigator/tests/tests_connector/test_settings.py | Unit tests for settings validation behavior. |
| external-import/corelight-investigator/tests/tests_connector/test_converter.py | Unit tests for severity mapping, incident creation, observable creation. |
| external-import/corelight-investigator/tests/tests_connector/test_connector.py | Unit tests for connector orchestration, bundling, and state updates. |
| external-import/corelight-investigator/Dockerfile | Container build for the connector. |
| external-import/corelight-investigator/entrypoint.sh | Container entrypoint wrapper. |
| external-import/corelight-investigator/docker-compose.yml | Example deployment configuration. |
| external-import/corelight-investigator/config.yml.sample | Manual deployment sample configuration. |
| external-import/corelight-investigator/README.md | Connector documentation and configuration reference. |
| external-import/corelight-investigator/metadata/connector_manifest.json | Connector marketplace/manifest metadata. |
| external-import/corelight-investigator/metadata/connector_config_schema.json | Generated config schema for the connector’s env vars. |
| external-import/corelight-investigator/metadata/CONNECTOR_CONFIG_DOC.md | Generated connector configuration documentation. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #6737 +/- ##
===========================================
+ Coverage 35.64% 86.56% +50.91%
===========================================
Files 2048 8 -2040
Lines 125310 588 -124722
===========================================
- Hits 44672 509 -44163
+ Misses 80638 79 -80559
📢 Thoughts on this report? Let us know! 🚀 New features to boost your workflow:
|
Review-and-fix pass summaryIndependent senior re-review of the full new connector plus the two open Copilot threads. Code fixes (commit 40c75e5):
Tests (commits 40c75e5, 9988e06): added coverage for the distinct TLP:CLEAR marking, the deterministic Incident id, the source-timestamp passthrough, Status: all CI checks are green (codecov/patch 88.44%, target 80%), Remaining (non-CI) blocker: |
Review-and-fix pass summaryIndependent senior re-review of the full
Remaining (non-CI) blocker: |
0d040c3 to
c8a586e
Compare
Review-and-fix pass summary (rebase + pycti alignment)Full pass on the
Remaining (non-CI) blocker: GitHub refuses self-approval ("Can not approve your own pull request"), so |
…erts and detections (#6736) Add an external-import connector for Corelight Investigator (SaaS NDR). It periodically queries the Investigator Detections and Alerts API (bearer API key), maps each alert/detection to a STIX Incident with the normalized severity (1-10) mapped to the OpenCTI severity, extracts the source/destination IP observables and relates them to the Incident, and attributes everything to a Corelight Investigator author identity with a configurable TLP. Imports are incremental via connector state. The query endpoint path is configurable (the in-product API reference documents the exact paths). Includes client + converter + connector unit tests, generated config schema/doc, README, manifest, config sample and docker-compose.
…ident ids Address the Copilot review on the new connector plus an independent senior review. - converter: TLP:CLEAR is no longer aliased to stix2.TLP_WHITE. CLEAR and AMBER+STRICT are now built as custom statement markings through a shared _statement_marking helper (matching connectors-sdk TLPMarking.to_stix2_object), so OpenCTI shows the correct TLP label instead of TLP:WHITE / TLP:AMBER. - converter: Incident.generate_id is seeded with the source timestamp only. When an alert has no usable timestamp the STIX created/modified still fall back to "now", but the id seed is None, so a re-imported alert keeps a stable Incident id instead of creating a duplicate on every run. - connector: finalize the initiated work with in_error=True on the API-error and generic-exception paths, so a failed run no longer leaves a dangling in-progress work in OpenCTI. - client/deps: type _request params as Optional[dict]; drop a stray space in the pydantic version pin. - tests: cover the distinct TLP:CLEAR marking, the deterministic incident id, the source-timestamp passthrough, _parse_timestamp, and the in-error work finalize.
…doc fixes - converter_to_stix.py: seed Incident.generate_id with the alert id in the name (not just the display name), so distinct alerts that share a name no longer collapse into one Incident. When the alert has no usable timestamp, created/modified now fall back to a fixed sentinel instead of "now", so a re-imported alert keeps a stable id and is not re-sent with drifting created/modified each run. - docs: mark scope / CONNECTOR_SCOPE mandatory in config.yml.sample and docker-compose.yml (required by the generated schema), and align the README minimum version to OpenCTI >= 6.8.12 (matching the manifest support_version). Covered by new converter tests for the distinct-id and stable-timestamp behavior.
…ToC and logging snippet
- Pin pycti==7.260701.0 in src/requirements.txt (latest release, aligned with connectors-sdk on master) and update the README pycti example.
- Fix the README Table of Contents anchor to match the actual H1 heading.
- Fix the README logging example to use attribute access (connector_logger.{LOG_LEVEL}) instead of an invalid comma.
…ted compliance
- set "verified": false and "last_verified_date": null in the manifest: this
is community-contributed work, not verified by Filigran (the VC201 linter
findings are expected and accepted)
- set "manager_supported": true and "support_version": ">=7.260701.0" in the
manifest, in sync with the pinned pycti
- replace the entrypoint.sh indirection with a direct python ENTRYPOINT in
the Dockerfile (VC402)
- give CONNECTOR_SCOPE a default ('corelight-investigator') so only the URL,
token and API credentials remain mandatory; regenerate
connector_config_schema.json and CONNECTOR_CONFIG_DOC.md with the SDK
tooling
- align docker-compose.yml and config.yml.sample with the sample conventions:
exact ChangeMe placeholders, defaulted variables commented out (VC104)
- only initiate a work when the run actually collected data, so empty runs do
not create zero-bundle works (VC317); adjust the connector tests
- update the README requirements and configuration tables accordingly
c8a586e to
461aebf
Compare
Proposed changes
New external-import connector for Corelight Investigator (SaaS NDR).
On a schedule the connector:
EventTyperecorded as the incident type;related-torelationships from the Incident to those observables;Related issues
Closes #6736
Types of changes
Checklist
black,isort) and linting (flake8, STIX id pylint) pass__metadata__config schema and documentation (via the SDK schema tooling)README.md,config.yml.sample,docker-compose.ymlandconnector_manifest.jsonFurther comments
The exact REST path/parameters of the Investigator Detections and Alerts API are documented in the in-product API reference (require a tenant account), so the query endpoint path is configurable via
alerts_pathwith a documented default, and the response parser accepts the common envelope keys (data/alerts/detections/results). This is the import side of a Corelight integration; the Corelight open sensor consumes intel via Zeek/TAXII (e.g. OpenCTI's native TAXII2 live feed) rather than an outbound IOC API.Community status and manager support
This connector is community-contributed work and is NOT officially verified by Filigran: the manifest intentionally ships
"verified": falseand"last_verified_date": null. Do not flipverifiedback totrueas part of this PR. Note: the VC201 verified-status check has been removed from the connector-linter onmaster, so the advisory Connector Verified Linter job passes cleanly with this community status.The connector IS manager-supported (
"manager_supported": true): configuration is loaded through the connectors-sdk pydantic-settings mechanism (env vars + optionalconfig.yml),__metadata__/connector_config_schema.jsonis generated from the settings model with the SDK schema generator andCONNECTOR_CONFIG_DOC.mdis generated from that schema. Manager-compatibility pass applied on top of the initial review:connector_manifest.json:verified: false,manager_supported: true,support_version: >=7.260701.0(in sync with the pinned pycti).Dockerfile: directENTRYPOINT ["python", "main.py"]instead of theentrypoint.shindirection (VC402);entrypoint.shremoved.CONNECTOR_SCOPEnow has a default (corelight-investigator), so the only mandatory variables are the OpenCTI URL/token and the Corelight API base URL/key; schema and doc regenerated accordingly.docker-compose.yml/config.yml.sample: exactChangeMeplaceholders, defaulted variables commented out (VC104 sample conventions).connector.py: a work is only initiated when the run actually collected data, so empty runs no longer create zero-bundle works (VC317).Maintainer review (independent review-and-fix)
Independent senior review of the full connector, on top of the automated review threads. Substantive fixes:
converter_to_stix.py: TLP:CLEAR and TLP:AMBER+STRICT are built as custom statement MarkingDefinitions via a shared_statement_markinghelper (matching connectors-sdkTLPMarking.to_stix2_object), so OpenCTI shows the correct TLP label instead of aliasingstix2.TLP_WHITE/TLP_AMBER.converter_to_stix.py(deterministic ids):Incident.generate_idis seeded with the alert id in the name (so distinct alerts that share a name do not collapse into one Incident) plus the source timestamp; when an alert has no usable timestamp,created/modifiedfall back to a fixed sentinel instead of "now", so a re-imported alert keeps a stable id and is not re-sent with drifting timestamps each run.connector.py: works are finalized within_error=Truewhen a failure occurs after the work was initiated; a fetch failure raises (the client raises after retries), solast_runis not advanced on error and the next run retries.scope/log_level/duration_periodoptional with defaults); the README Table of Contents anchor and logging example fixed.pyctipinned to the latest release7.260701.0(aligned withconnectors-sdkonmaster, which is installed frommaster, never a pinned tag).Tests
Unit tests cover the settings model, the API client (request shaping, retries, error wrapping), the converter (TLP:CLEAR / AMBER+STRICT marking shapes, severity mapping,
_parse_timestamp, deterministic and collision-free incident ids, stable fallback timestamps, IP observable extraction + relationships) and the connector (no work initiated on empty runs, work finalizedin_errorwhen the bundle send fails). 44 unit tests pass;black/isort/flake8clean locally.Status
The branch is rebased onto the latest
master(linear history, all commits GPG-signed). All CI checks are green, including the advisory Connector Verified Linter job (36/36 checks on this connector after VC201 removal upstream). 0 unresolved review threads.