Skip to content

Commit ac06a00

Browse files
committed
feat: automated migration
1 parent d633724 commit ac06a00

File tree

10 files changed

+518
-69
lines changed

10 files changed

+518
-69
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://www.filigran.io/connectors/microsoft-sentinel-intel_config.schema.json",
4+
"type": "object",
5+
"properties": {
6+
"OPENCTI_URL": {
7+
"description": "The base URL of the OpenCTI instance.",
8+
"format": "uri",
9+
"maxLength": 2083,
10+
"minLength": 1,
11+
"type": "string"
12+
},
13+
"OPENCTI_TOKEN": {
14+
"description": "The API token to connect to OpenCTI.",
15+
"type": "string"
16+
},
17+
"CONNECTOR_NAME": {
18+
"default": "MicrosoftSentinelIntel",
19+
"description": "The name of the connector.",
20+
"type": "string"
21+
},
22+
"CONNECTOR_SCOPE": {
23+
"description": "The scope of the connector, e.g. 'flashpoint'.",
24+
"items": {
25+
"type": "string"
26+
},
27+
"type": "array"
28+
},
29+
"CONNECTOR_LOG_LEVEL": {
30+
"default": "error",
31+
"description": "The minimum level of logs to display.",
32+
"enum": [
33+
"debug",
34+
"info",
35+
"warn",
36+
"warning",
37+
"error"
38+
],
39+
"type": "string"
40+
},
41+
"CONNECTOR_TYPE": {
42+
"const": "STREAM",
43+
"default": "STREAM",
44+
"type": "string"
45+
},
46+
"CONNECTOR_LIVE_STREAM_ID": {
47+
"default": "live",
48+
"description": "The ID of the live stream to connect to.",
49+
"type": "string"
50+
},
51+
"CONNECTOR_LIVE_STREAM_LISTEN_DELETE": {
52+
"default": true,
53+
"description": "Whether to listen for delete events on the live stream.",
54+
"type": "boolean"
55+
},
56+
"CONNECTOR_LIVE_STREAM_NO_DEPENDENCIES": {
57+
"default": true,
58+
"description": "Whether to ignore dependencies when processing events from the live stream.",
59+
"type": "boolean"
60+
},
61+
"MICROSOFT_SENTINEL_INTEL_TENANT_ID": {
62+
"description": "Your Azure App Tenant ID, see the screenshot to help you find this information.",
63+
"type": "string"
64+
},
65+
"MICROSOFT_SENTINEL_INTEL_CLIENT_ID": {
66+
"description": "Your Azure App Client ID, see the screenshot to help you find this information.",
67+
"type": "string"
68+
},
69+
"MICROSOFT_SENTINEL_INTEL_CLIENT_SECRET": {
70+
"description": "Your Azure App Client secret, See the screenshot to help you find this information.",
71+
"format": "password",
72+
"type": "string",
73+
"writeOnly": true
74+
},
75+
"MICROSOFT_SENTINEL_INTEL_WORKSPACE_ID": {
76+
"description": "Your Azure Workspace ID",
77+
"type": "string"
78+
},
79+
"MICROSOFT_SENTINEL_INTEL_WORKSPACE_NAME": {
80+
"description": "The name of the log analytics workspace",
81+
"type": "string"
82+
},
83+
"MICROSOFT_SENTINEL_INTEL_SUBSCRIPTION_ID": {
84+
"description": "The subscription id where the Log Analytics is",
85+
"type": "string"
86+
},
87+
"MICROSOFT_SENTINEL_INTEL_RESOURCE_GROUP": {
88+
"default": "default",
89+
"deprecated": true,
90+
"description": "The name of the resource group where the log analytics is",
91+
"type": "string"
92+
},
93+
"MICROSOFT_SENTINEL_INTEL_SOURCE_SYSTEM": {
94+
"default": "Opencti Stream Connector",
95+
"description": "The name of the source system displayed in Microsoft Sentinel",
96+
"type": "string"
97+
},
98+
"MICROSOFT_SENTINEL_INTEL_DELETE_EXTENSIONS": {
99+
"default": true,
100+
"description": "Delete the extensions in the stix bundle sent to the SIEM",
101+
"type": "boolean"
102+
},
103+
"MICROSOFT_SENTINEL_INTEL_EXTRA_LABELS": {
104+
"default": [],
105+
"description": "Extra labels added to the bundle sent. String separated by comma",
106+
"items": {
107+
"type": "string"
108+
},
109+
"type": "array"
110+
},
111+
"MICROSOFT_SENTINEL_INTEL_WORKSPACE_API_VERSION": {
112+
"default": "2024-02-01-preview",
113+
"description": "API version of the Microsoft log analytics workspace interface",
114+
"type": "string"
115+
},
116+
"MICROSOFT_SENTINEL_INTEL_MANAGEMENT_API_VERSION": {
117+
"default": "2025-03-01",
118+
"description": "API version of the Microsoft management interface",
119+
"type": "string"
120+
}
121+
},
122+
"required": [
123+
"OPENCTI_URL",
124+
"OPENCTI_TOKEN",
125+
"CONNECTOR_SCOPE",
126+
"MICROSOFT_SENTINEL_INTEL_TENANT_ID",
127+
"MICROSOFT_SENTINEL_INTEL_CLIENT_ID",
128+
"MICROSOFT_SENTINEL_INTEL_CLIENT_SECRET",
129+
"MICROSOFT_SENTINEL_INTEL_WORKSPACE_ID",
130+
"MICROSOFT_SENTINEL_INTEL_WORKSPACE_NAME",
131+
"MICROSOFT_SENTINEL_INTEL_SUBSCRIPTION_ID"
132+
],
133+
"additionalProperties": true
134+
}

stream/microsoft-sentinel-intel/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ services:
2626
# - MICROSOFT_SENTINEL_INTEL_EXTRA_LABELS=label1,label2,...
2727
# - MICROSOFT_SENTINEL_INTEL_WORKSPACE_API_VERSION=2024-02-01-preview
2828
# - MICROSOFT_SENTINEL_INTEL_MANAGEMENT_API_VERSION=2025-03-01
29-
restart: unless-stopped
29+
restart: unless-stopped

stream/microsoft-sentinel-intel/src/base_connector/config.py

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,7 @@
2121
YamlConfigSettingsSource,
2222
)
2323

24-
"""
25-
All the variables that have default values will override configuration from the OpenCTI helper.
26-
27-
All the variables of this classes are customizable through:
28-
- config.yml
29-
- .env
30-
- environment variables.
31-
32-
If a variable is set in 2 different places, the first one will be used in this order:
33-
1. YAML file
34-
2. .env file
35-
3. Environment variables
36-
4. Default value
37-
38-
WARNING:
39-
The Environment variables in the .env or global environment must be set in the following format:
40-
OPENCTI_<variable>
41-
CONNECTOR_<variable>
42-
43-
the split is made on the first occurrence of the "_" character.
44-
"""
24+
'\nAll the variables that have default values will override configuration from the OpenCTI helper.\n\nAll the variables of this classes are customizable through:\n - config.yml \n - .env\n - environment variables.\n\nIf a variable is set in 2 different places, the first one will be used in this order:\n 1. YAML file\n 2. .env file\n 3. Environment variables\n 4. Default value\n \nWARNING:\n The Environment variables in the .env or global environment must be set in the following format:\n OPENCTI_<variable>\n CONNECTOR_<variable>\n \n the split is made on the first occurrence of the "_" character.\n'
4525

4626

4727
def environ_list_validator(value: str | list[str]) -> list[str]:
@@ -51,13 +31,13 @@ def environ_list_validator(value: str | list[str]) -> list[str]:
5131

5232

5333
def pycti_list_serializer(v: list[str], info: SerializationInfo) -> str | list[str]:
54-
if isinstance(v, list) and info.context and info.context.get("mode") == "pycti":
55-
return ",".join(v) # [ "e1", "e2", "e3" ] -> "e1,e2,e3"
34+
if isinstance(v, list) and info.context and (info.context.get("mode") == "pycti"):
35+
return ",".join(v)
5636
return v
5737

5838

5939
ListFromString = Annotated[
60-
list[str], # Final type
40+
list[str],
6141
BeforeValidator(environ_list_validator),
6242
PlainSerializer(pycti_list_serializer, when_used="json"),
6343
]
@@ -76,21 +56,17 @@ class StreamConnectorConfig(BaseModel):
7656
type: Literal["STREAM"] = Field(default="STREAM")
7757
scope: ListFromString
7858
log_level: LogLevelType
79-
8059
live_stream_id: str
8160
live_stream_listen_delete: bool = Field(default=True)
8261
live_stream_no_dependencies: bool = Field(default=True)
8362
live_stream_with_inferences: bool = Field(default=False)
8463
live_stream_recover_iso_date: str | None = Field(default=None)
8564
live_stream_start_timestamp: str | None = Field(default=None)
86-
8765
listen_protocol: str = Field(default="AMQP")
8866
listen_protocol_uri: str = Field(default="http://127.0.0.1:7070")
8967
listen_protocol_path: str = Field(default="/api/callback")
9068
listen_protocol_ssl: bool = Field(default=False)
9169
listen_protocol_port: int = Field(default=7070)
92-
93-
# TODO: See what is specific to stream / external
9470
auto: bool = Field(default=False)
9571
expose_metrics: bool = Field(default=False)
9672
metrics_port: int = Field(default=9095)
@@ -99,7 +75,6 @@ class StreamConnectorConfig(BaseModel):
9975
validate_before_import: bool = Field(default=False)
10076
queue_protocol: str = Field(default="amqp")
10177
queue_threshold: int = Field(default=500)
102-
10378
send_to_queue: bool = Field(default=True)
10479
send_to_directory: bool = Field(default=False)
10580
send_to_directory_path: str | None = Field(default=None)
@@ -128,8 +103,6 @@ def validate_live_stream_id(cls, v: str) -> str:
128103
class BaseConnectorSettings(abc.ABC, BaseSettings):
129104
opencti: _OpenCTIConfig
130105
connector: StreamConnectorConfig
131-
132-
# files needs to be at the same level as the module
133106
model_config = SettingsConfigDict(
134107
env_nested_delimiter="_", env_nested_max_split=1, enable_decoding=False
135108
)
@@ -160,9 +133,9 @@ def settings_customise_sources(
160133
3. Environment variables
161134
4. Default values
162135
"""
163-
if Path(settings_cls.model_config["yaml_file"] or "").is_file(): # type: ignore
136+
if Path(settings_cls.model_config["yaml_file"] or "").is_file():
164137
return (YamlConfigSettingsSource(settings_cls),)
165-
if Path(settings_cls.model_config["env_file"] or "").is_file(): # type: ignore
138+
if Path(settings_cls.model_config["env_file"] or "").is_file():
166139
return (dotenv_settings,)
167140
return (env_settings,)
168141

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import traceback
22

3-
from microsoft_sentinel_intel import Connector
3+
from microsoft_sentinel_intel import Connector, ConnectorSettings
44
from microsoft_sentinel_intel.client import ConnectorClient
5-
from microsoft_sentinel_intel.config import ConnectorSettings
65
from pycti import OpenCTIConnectorHelper
76

8-
9-
def main() -> None:
7+
if __name__ == "__main__":
108
"""
119
Entry point of the script
1210
@@ -16,17 +14,13 @@ def main() -> None:
1614
- exit(1): effective way to terminate a Python program when an error is encountered.
1715
It signals to the operating system and any calling processes that the program did not complete successfully.
1816
"""
19-
config = ConnectorSettings()
20-
helper = OpenCTIConnectorHelper(config=config.model_dump_pycti())
21-
client = ConnectorClient(helper=helper, config=config)
22-
23-
connector = Connector(helper=helper, config=config, client=client)
24-
connector.run()
25-
26-
27-
if __name__ == "__main__":
2817
try:
29-
main()
18+
settings = ConnectorSettings()
19+
helper = OpenCTIConnectorHelper(config=settings.to_helper_config())
20+
client = ConnectorClient(helper=helper, config=settings)
21+
22+
connector = Connector(config=settings, helper=helper, client=client)
23+
connector.run()
3024
except Exception:
3125
traceback.print_exc()
3226
exit(1)
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
from microsoft_sentinel_intel.client import ConnectorClient
2-
from microsoft_sentinel_intel.config import ConnectorSettings
32
from microsoft_sentinel_intel.connector import Connector
3+
from microsoft_sentinel_intel.settings import ConnectorSettings
44

5-
__all__ = [
6-
"Connector",
7-
"ConnectorClient",
8-
"ConnectorSettings",
9-
]
5+
__all__ = ["Connector", "ConnectorClient", "ConnectorSettings"]

stream/microsoft-sentinel-intel/src/microsoft_sentinel_intel/connector.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,14 @@
44

55
from filigran_sseclient.sseclient import Event
66
from microsoft_sentinel_intel.client import ConnectorClient
7-
from microsoft_sentinel_intel.config import ConnectorSettings
8-
from microsoft_sentinel_intel.errors import (
9-
ConnectorError,
10-
ConnectorWarning,
11-
)
7+
from microsoft_sentinel_intel.errors import ConnectorError, ConnectorWarning
8+
from microsoft_sentinel_intel.settings import ConnectorSettings
129
from microsoft_sentinel_intel.utils import is_stix_indicator
1310
from pycti import OpenCTIConnectorHelper
1411

1512

1613
class Connector:
14+
1715
def __init__(
1816
self,
1917
helper: OpenCTIConnectorHelper,
@@ -27,12 +25,10 @@ def __init__(
2725
def _prepare_stix_object(self, stix_object: dict) -> dict:
2826
if self.config.microsoft_sentinel_intel.delete_extensions:
2927
del stix_object["extensions"]
30-
3128
if extra_labels := self.config.microsoft_sentinel_intel.extra_labels:
3229
stix_object["labels"] = list(
3330
set(stix_object.get("labels", []) + extra_labels)
3431
)
35-
3632
return stix_object
3733

3834
def _process_event(self, event_type: str, stix_object: dict) -> None:
@@ -63,14 +59,12 @@ def _handle_event(self, event: Event):
6359
message="[ERROR] Data cannot be parsed to JSON",
6460
metadata={"message_data": event.data, "error": str(err)},
6561
) from err
66-
6762
if is_stix_indicator(data):
6863
self.helper.connector_logger.info(
6964
message=f"[{event.event.upper()}] Processing message",
7065
meta={"data": data, "event": event.event},
7166
)
7267
self._process_event(event_type=event.event, stix_object=data)
73-
7468
self.helper.connector_logger.info(
7569
message=f"[{event.event.upper()}] Indicator processed",
7670
meta={"opencti_id": data["id"]},
@@ -96,15 +90,11 @@ def process_message(self, message: Event) -> None:
9690
except ConnectorWarning as err:
9791
self.helper.connector_logger.warning(message=err.message)
9892
except ConnectorError as err:
99-
self.helper.connector_logger.error(
100-
message=err.message,
101-
meta=err.metadata,
102-
)
93+
self.helper.connector_logger.error(message=err.message, meta=err.metadata)
10394
except Exception as err:
10495
traceback.print_exc()
10596
self.helper.connector_logger.error(
106-
message=f"Unexpected error: {err}",
107-
meta={"error": str(err)},
97+
message=f"Unexpected error: {err}", meta={"error": str(err)}
10898
)
10999

110100
def run(self) -> None:

0 commit comments

Comments
 (0)