High-performance Rust implementation for syncing threat intelligence from MISP to Microsoft Sentinel.
This project supports the STIX Objects version of the Microsoft Sentinel Upload Indicators API and requires the following to be setup
Application registration in Entra ID with Microsoft Sentinel Contributor on the resource group where Microsoft Sentinel is deployed.
- You should have the following values from this step:
- client_id - the application id of the application registration
- client_secret - the secret (value) of the application registration, must be created
- tenant_id - the azure tenant id
- workspace_id - the id of sentinels underlying log analytics workspace
For more guidance the Entra ID aspects:
This one is probably pretty obvious, but we need a MISP-server with a valid URI and auth key.
- You should have the following values from this step:
- MISP URI - the url of the MISP server
- api key - misp auth key
For more guidance on MISP, check out:
- Download the appropriate binary for your platform from GitHub Releases
- Copy the
config.toml.examplefile and place it in the same directory as the binary (or you can use environment variables)
cp config.toml.example config.toml- Fill out the config file with values from the prereqs and then check the config file
./misp2sentinel-cli --config config.toml --check-config- Run the binary
# Base config from file
./misp2sentinel-cli --config config.toml
# With env override for API key (e.g., from secret manager)
MISP_API_KEY=$(vault read -field=key secret/misp) ./misp2sentinel-cli --config config.tomlRunning locally on my Windows desktop against remote MISP-server, the config.toml file is placed in the same folder:
[misp]
url = "https://misp.example"
api_key = "if your eyes arent real"
verify_tls = false
[misp.filters]
publish_timestamp = "1d"
published = true
to_ids = true
[sentinel]
workspace_id = "what"
tenant_id = "do"
client_id = "tigers"
client_secret = "dream of?"
[sync]
dry_run = false
batch_size = 100
requests_per_minute = 100
write_parsed_indicators = true
write_parsed_event_ids = trueRun .exe in pwsh:
.\misp2sentinel-cli-windows-x64.exeOutput as follows:
- Filter Configuration Guide - How to configure MISP event filters
- Local/CLI Setup - Running locally on the MISP server
- Architecture - System architecture and design
| Crate | Description |
|---|---|
misp2sentinel-core |
Core library |
misp2sentinel-cli |
CLI tool |
| Variable | Required | Description |
|---|---|---|
MISP_URL |
Yes | MISP instance URL |
MISP_API_KEY |
Yes | MISP API key |
MISP_VERIFY_TLS |
No | Verify SSL certificates (default: true, set to false for self-signed) |
SENTINEL_WORKSPACE_ID |
Yes | Sentinel workspace ID |
AZURE_TENANT_ID |
Yes | The Azure Tenant ID |
AZURE_CLIENT_ID |
Yes | Application ID of the app registration |
AZURE_CLIENT_SECRET |
Yes | Application secret of the app registration |
Control which events are synced using environment variables.
Built-in defaults (no configuration required, but can be changed in config.toml or via env):
published: true- Only syncs published eventspublish_timestamp: 14d- Only events published in last 14 daysto_ids: true- Only IDS-flagged attributes
Optional overrides:
| Variable | Example | Description |
|---|---|---|
MISP_FILTER_TAGS |
tlp:white,type:malware |
Include events with these tags (OR logic) |
MISP_FILTER_NOT_TAGS |
tlp:red,false-positive |
Exclude events with these tags |
MISP_FILTER_ORGS |
CIRCL,MISP Project |
Include events from these organizations |
MISP_FILTER_NOT_ORGS |
Test Org |
Exclude events from these organizations |
MISP_FILTER_PUBLISH_TIMESTAMP |
7d or 2024-01-01 |
Events published after this time (default: 14d) |
MISP_FILTER_LAST |
24h or 7d |
Events from the last N time period |
MISP_FILTER_PUBLISHED |
true |
Only published events (default: true) |
MISP_FILTER_TO_IDS |
true |
Only IDS-flagged attributes (default: true) |
MISP_FILTER_THREAT_LEVEL |
1,2 |
Filter by threat level (1=High, 2=Medium, 3=Low) |
MISP_FILTER_EVENT_INFO |
phishing |
Search in event description |
See docs/FILTERS.md for full configuration options.
The Rust implementation is significantly faster than the Python version, primarily due to concurrent uploads and highly optimized STIX conversion.
| Metric | Python | Rust | Notes |
|---|---|---|---|
| Events fetched | 10 | 10 | Same source |
| Indicators created | 1507 | 1496 | ~0.7% variance (see below) |
| Upload success | 1507 | 1496 | Both 100% |
| Upload errors | 0 | 0 | N/A |
| Phase | Python | Rust | Speedup |
|---|---|---|---|
| MISP Fetch | 0.73s | 0.53s | 1.4x |
| STIX Conversion | 1.21s | 0.003s | ~400x |
| Azure Auth | 0.44s | 0.15s | 2.9x |
| Sentinel Upload | 120.23s | 23.31s | 5.2x |
| TOTAL | 122.61s | 23.85s | 5.14x faster |
- Concurrent uploads: Rust uploads 4 batches in parallel vs Python's sequential approach
- Native STIX conversion: Rust converts attributes directly to STIX patterns without external libraries (449,074 indicators/sec)
- Zero-copy parsing: Efficient JSON handling with serde
The slight variance (1507 vs 1496 = 11 indicators, ~0.7%) is due to implementation differences:
| Difference | Python | Rust |
|---|---|---|
| STIX converter | Uses misp-stix library (Python) |
Native Rust implementation |
| Deduplication | Per-event dedup, then global | Pattern-based global dedup |
| Attribute handling | Library-determined type mapping | Direct attribute type → STIX pattern |
| Edge cases | Library handles composite attributes | Some composite types may be handled differently |
Both implementations produce valid STIX indicators that Sentinel accepts (100% upload success rate). The difference is negligible for production use.
MIT
Caution
Disclaimer
This tool is created mainly by using AI. Please exercise caution when using this solution and always understand what are you running before you run it in production. This tool was created as an experiment to learn more about Rust - the developer assumes no liability for any vulnerabilities or issues.
By downloading, installing, or using this tool, you acknowledge that you have read, understood, and agree to these terms.
