security-detections-mcp 3.0.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -21
- package/dist/db/detections.d.ts +8 -8
- package/dist/db/detections.js +14 -7
- package/dist/db/schema.js +15 -9
- package/dist/db.d.ts +3 -3
- package/dist/db.js +27 -15
- package/dist/index.js +5 -3
- package/dist/indexer.d.ts +5 -1
- package/dist/indexer.js +40 -2
- package/dist/parsers/crowdstrike_cql.d.ts +2 -0
- package/dist/parsers/crowdstrike_cql.js +302 -0
- package/dist/parsers/elastic.js +3 -0
- package/dist/parsers/kql.js +6 -0
- package/dist/parsers/sigma.js +3 -0
- package/dist/parsers/splunk.js +3 -0
- package/dist/parsers/sublime.d.ts +2 -0
- package/dist/parsers/sublime.js +106 -0
- package/dist/resources/index.js +1 -1
- package/dist/tools/detections/analysis.js +4 -4
- package/dist/tools/detections/comparison.js +3 -3
- package/dist/tools/detections/filters.js +1 -1
- package/dist/tools/detections/search.js +1 -1
- package/dist/tools/engineering/index.js +2 -2
- package/dist/types/detection.d.ts +46 -4
- package/dist/types/detection.js +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +1 -1
- package/dist/types/stats.d.ts +1 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
# Security Detections MCP
|
|
2
2
|
|
|
3
|
-
An MCP (Model Context Protocol) server that lets LLMs query a unified database of **Sigma**, **Splunk ESCU**, **Elastic**, and **
|
|
3
|
+
An MCP (Model Context Protocol) server that lets LLMs query a unified database of **Sigma**, **Splunk ESCU**, **Elastic**, **KQL**, **Sublime**, and **CrowdStrike CQL** security detection rules.
|
|
4
4
|
|
|
5
5
|
> **New here? Start with the [Setup Guide](./SETUP.md)** -- covers macOS, Windows (WSL & native), and Linux step by step.
|
|
6
6
|
|
|
7
|
+
[](https://cursor.com/en/install-mcp?name=security-detections&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInNlY3VyaXR5LWRldGVjdGlvbnMtbWNwIl0sImVudiI6eyJTSUdNQV9QQVRIUyI6Ii9wYXRoL3RvL3NpZ21hL3J1bGVzLC9wYXRoL3RvL3NpZ21hL3J1bGVzLXRocmVhdC1odW50aW5nIiwiU1BMVU5LX1BBVEhTIjoiL3BhdGgvdG8vc2VjdXJpdHlfY29udGVudC9kZXRlY3Rpb25zIiwiU1RPUllfUEFUSFMiOiIvcGF0aC90by9zZWN1cml0eV9jb250ZW50L3N0b3JpZXMiLCJFTEFTVElDX1BBVEhTIjoiL3BhdGgvdG8vZGV0ZWN0aW9uLXJ1bGVzL3J1bGVzIiwiS1FMX1BBVEhTIjoiL3BhdGgvdG8va3FsLXJ1bGVzIiwiU1VCTElNRV9QQVRIUyI6Ii9wYXRoL3RvL3N1YmxpbWUtcnVsZXMvZGV0ZWN0aW9uLXJ1bGVzIiwiQ1FMX0hVQl9QQVRIUyI6Ii9wYXRoL3RvL2NxbC1odWIvcXVlcmllcyJ9fQ==)
|
|
8
|
+
|
|
9
|
+
## What's New in 3.1 - New Detection Sources
|
|
10
|
+
|
|
11
|
+
- **CrowdStrike CQL Hub** - Query and search CrowdStrike Query Language (CQL) detections from the CQL Hub community repository
|
|
12
|
+
- **Sublime Rules** - Query and search Sublime Security detection rules for email-based threats
|
|
13
|
+
|
|
7
14
|
## What's New in 3.0 - Autonomous Detection Platform
|
|
8
15
|
|
|
9
16
|
Version 3.0 transforms this MCP into a **fully autonomous detection engineering platform**. Feed it threat intelligence, and it automatically:
|
|
@@ -91,10 +98,6 @@ The autonomous pipeline integrates with existing MCPs:
|
|
|
91
98
|
|
|
92
99
|
See the [Autonomous Platform Documentation](./docs/AUTONOMOUS.md) for full details, and the [E2E Testing Guide](./docs/E2E-TESTING-GUIDE.md) for per-SIEM setup (Splunk, Sentinel, Elastic, Sigma).
|
|
93
100
|
|
|
94
|
-
[](https://cursor.com/en/install-mcp?name=security-detections&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInNlY3VyaXR5LWRldGVjdGlvbnMtbWNwIl0sImVudiI6eyJTSUdNQV9QQVRIUyI6Ii9wYXRoL3RvL3NpZ21hL3J1bGVzLC9wYXRoL3RvL3NpZ21hL3J1bGVzLXRocmVhdC1odW50aW5nIiwiU1BMVU5LX1BBVEhTIjoiL3BhdGgvdG8vc2VjdXJpdHlfY29udGVudC9kZXRlY3Rpb25zIiwiU1RPUllfUEFUSFMiOiIvcGF0aC90by9zZWN1cml0eV9jb250ZW50L3N0b3JpZXMiLCJFTEFTVElDX1BBVEhTIjoiL3BhdGgvdG8vZGV0ZWN0aW9uLXJ1bGVzL3J1bGVzIiwiS1FMX1BBVEhTIjoiL3BhdGgvdG8va3FsLXJ1bGVzIn19)
|
|
95
|
-
|
|
96
|
-
> **Detailed setup**: See the **[Setup Guide](./SETUP.md)** for step-by-step install on macOS, Windows (WSL & native), and Linux with troubleshooting for common issues.
|
|
97
|
-
|
|
98
101
|
## 🐛 Version 2.1.1 (Bug Fix)
|
|
99
102
|
|
|
100
103
|
- **Fixed Windows EBUSY crash** - SQLite database recreation now handles Windows file locking with retry logic. Previously, Windows users would get `EBUSY: resource busy or locked` on startup.
|
|
@@ -213,7 +216,7 @@ Claude will:
|
|
|
213
216
|
- **🆕 Server Instructions** - Built-in usage guide with examples for better LLM understanding
|
|
214
217
|
- **🆕 Structured Errors** - Helpful error messages with suggestions and similar items
|
|
215
218
|
- **🆕 Interactive Tools** - Gap prioritization and sprint planning with form-based input (Cursor 0.42+)
|
|
216
|
-
- **Unified Search** - Query Sigma, Splunk ESCU, Elastic, and
|
|
219
|
+
- **Unified Search** - Query Sigma, Splunk ESCU, Elastic, KQL, Sublime, and CrowdStrike CQL detections from a single interface
|
|
217
220
|
- **Full-Text Search** - SQLite FTS5 powered search across names, descriptions, queries, MITRE tactics, CVEs, process names, and more
|
|
218
221
|
- **MITRE ATT&CK Mapping** - Filter detections by technique ID or tactic
|
|
219
222
|
- **CVE Coverage** - Find detections for specific CVE vulnerabilities
|
|
@@ -221,7 +224,7 @@ Claude will:
|
|
|
221
224
|
- **Analytic Stories** - Query by Splunk analytic story (optional - enhances context)
|
|
222
225
|
- **KQL Categories** - Filter KQL queries by category (Defender For Endpoint, Azure AD, Threat Hunting, etc.)
|
|
223
226
|
- **Auto-Indexing** - Automatically indexes detections on startup from configured paths
|
|
224
|
-
- **Multi-Format Support** - YAML (Sigma, Splunk), TOML (Elastic), Markdown (KQL)
|
|
227
|
+
- **Multi-Format Support** - YAML (Sigma, Splunk, Sublime, CrowdStrike CQL), TOML (Elastic), Markdown (KQL)
|
|
225
228
|
- **Logsource Filtering** - Filter Sigma rules by category, product, or service
|
|
226
229
|
- **Severity Filtering** - Filter by criticality level
|
|
227
230
|
|
|
@@ -261,7 +264,9 @@ Add to your MCP config (`~/.cursor/mcp.json` or `.cursor/mcp.json` in your proje
|
|
|
261
264
|
"SPLUNK_PATHS": "/path/to/security_content/detections",
|
|
262
265
|
"ELASTIC_PATHS": "/path/to/detection-rules/rules",
|
|
263
266
|
"STORY_PATHS": "/path/to/security_content/stories",
|
|
264
|
-
"KQL_PATHS": "/path/to/Hunting-Queries-Detection-Rules"
|
|
267
|
+
"KQL_PATHS": "/path/to/Hunting-Queries-Detection-Rules",
|
|
268
|
+
"SUBLIME_PATHS": "/path/to/sublime-rules/detection-rules",
|
|
269
|
+
"CQL_HUB_PATHS": "/path/to/cql-hub/queries"
|
|
265
270
|
}
|
|
266
271
|
}
|
|
267
272
|
}
|
|
@@ -283,7 +288,9 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
|
283
288
|
"SPLUNK_PATHS": "/Users/you/security_content/detections",
|
|
284
289
|
"ELASTIC_PATHS": "/Users/you/detection-rules/rules",
|
|
285
290
|
"STORY_PATHS": "/Users/you/security_content/stories",
|
|
286
|
-
"KQL_PATHS": "/Users/you/Hunting-Queries-Detection-Rules"
|
|
291
|
+
"KQL_PATHS": "/Users/you/Hunting-Queries-Detection-Rules",
|
|
292
|
+
"SUBLIME_PATHS": "/Users/you/sublime-rules/detection-rules",
|
|
293
|
+
"CQL_HUB_PATHS": "/Users/you/cql-hub/queries"
|
|
287
294
|
}
|
|
288
295
|
}
|
|
289
296
|
}
|
|
@@ -305,7 +312,9 @@ Add to `~/.vscode/mcp.json`:
|
|
|
305
312
|
"SPLUNK_PATHS": "/Users/you/security_content/detections",
|
|
306
313
|
"ELASTIC_PATHS": "/Users/you/detection-rules/rules",
|
|
307
314
|
"KQL_PATHS": "/Users/you/kql-bertjanp,/Users/you/kql-jkerai1",
|
|
308
|
-
"STORY_PATHS": "/Users/you/security_content/stories"
|
|
315
|
+
"STORY_PATHS": "/Users/you/security_content/stories",
|
|
316
|
+
"SUBLIME_PATHS": "/Users/you/sublime-rules/detection-rules",
|
|
317
|
+
"CQL_HUB_PATHS": "/Users/you/cql-hub/queries"
|
|
309
318
|
}
|
|
310
319
|
}
|
|
311
320
|
}
|
|
@@ -327,7 +336,9 @@ Add to `~/.vscode/mcp.json`:
|
|
|
327
336
|
"SPLUNK_PATHS": "/Users/you/security_content/detections",
|
|
328
337
|
"ELASTIC_PATHS": "/Users/you/detection-rules/rules",
|
|
329
338
|
"KQL_PATHS": "/Users/you/kql-bertjanp,/Users/you/kql-jkerai1",
|
|
330
|
-
"STORY_PATHS": "/Users/you/security_content/stories"
|
|
339
|
+
"STORY_PATHS": "/Users/you/security_content/stories",
|
|
340
|
+
"SUBLIME_PATHS": "/Users/you/sublime-rules/detection-rules",
|
|
341
|
+
"CQL_HUB_PATHS": "/Users/you/cql-hub/queries"
|
|
331
342
|
}
|
|
332
343
|
}
|
|
333
344
|
}
|
|
@@ -341,6 +352,8 @@ Add to `~/.vscode/mcp.json`:
|
|
|
341
352
|
| `SPLUNK_PATHS` | Comma-separated paths to Splunk ESCU detection directories | At least one source required |
|
|
342
353
|
| `ELASTIC_PATHS` | Comma-separated paths to Elastic detection rule directories | At least one source required |
|
|
343
354
|
| `KQL_PATHS` | Comma-separated paths to KQL hunting query directories | At least one source required |
|
|
355
|
+
| `SUBLIME_PATHS` | Comma-separated paths to Sublime Security rule directories | At least one source required |
|
|
356
|
+
| `CQL_HUB_PATHS` | Comma-separated paths to CQL Hub (CrowdStrike) query directories | At least one source required |
|
|
344
357
|
| `STORY_PATHS` | Comma-separated paths to Splunk analytic story directories | No (enhances context) |
|
|
345
358
|
|
|
346
359
|
## Getting Detection Content
|
|
@@ -369,11 +382,20 @@ cd detection-rules && git sparse-checkout set rules && cd ..
|
|
|
369
382
|
git clone --depth 1 https://github.com/Bert-JanP/Hunting-Queries-Detection-Rules.git kql-bertjanp
|
|
370
383
|
git clone --depth 1 https://github.com/jkerai1/KQL-Queries.git kql-jkerai1
|
|
371
384
|
|
|
385
|
+
# Download Sublime Security email detection rules (~900+ rules)
|
|
386
|
+
git clone --depth 1 --filter=blob:none --sparse https://github.com/sublime-security/sublime-rules.git
|
|
387
|
+
cd sublime-rules && git sparse-checkout set detection-rules && cd ..
|
|
388
|
+
|
|
389
|
+
# Download CQL Hub CrowdStrike queries (~139+ queries)
|
|
390
|
+
git clone --depth 1 https://github.com/ByteRay-Labs/Query-Hub.git cql-hub
|
|
391
|
+
|
|
372
392
|
echo "Done! Configure your MCP with these paths:"
|
|
373
393
|
echo " SIGMA_PATHS: $(pwd)/sigma/rules,$(pwd)/sigma/rules-threat-hunting"
|
|
374
394
|
echo " SPLUNK_PATHS: $(pwd)/security_content/detections"
|
|
375
395
|
echo " ELASTIC_PATHS: $(pwd)/detection-rules/rules"
|
|
376
396
|
echo " KQL_PATHS: $(pwd)/kql-bertjanp,$(pwd)/kql-jkerai1"
|
|
397
|
+
echo " SUBLIME_PATHS: $(pwd)/sublime-rules/detection-rules"
|
|
398
|
+
echo " CQL_HUB_PATHS: $(pwd)/cql-hub/queries"
|
|
377
399
|
echo " STORY_PATHS: $(pwd)/security_content/stories"
|
|
378
400
|
```
|
|
379
401
|
|
|
@@ -398,6 +420,14 @@ git clone https://github.com/elastic/detection-rules.git
|
|
|
398
420
|
git clone https://github.com/Bert-JanP/Hunting-Queries-Detection-Rules.git
|
|
399
421
|
git clone https://github.com/jkerai1/KQL-Queries.git
|
|
400
422
|
# Use entire repos, combine paths with comma
|
|
423
|
+
|
|
424
|
+
# Sublime Security Rules
|
|
425
|
+
git clone https://github.com/sublime-security/sublime-rules.git
|
|
426
|
+
# Use detection-rules/ directory
|
|
427
|
+
|
|
428
|
+
# CQL Hub (CrowdStrike Query Language)
|
|
429
|
+
git clone https://github.com/ByteRay-Labs/Query-Hub.git
|
|
430
|
+
# Use queries/ directory
|
|
401
431
|
```
|
|
402
432
|
|
|
403
433
|
## 🆕 MCP Resources - Readable Context
|
|
@@ -427,7 +457,7 @@ The server provides **autocomplete suggestions** as you type argument values:
|
|
|
427
457
|
| `process_name` | Process names in your detections (powershell.exe, etc.) |
|
|
428
458
|
| `tactic` | All 14 MITRE tactics |
|
|
429
459
|
| `severity` | informational, low, medium, high, critical |
|
|
430
|
-
| `source_type` | sigma, splunk_escu, elastic, kql |
|
|
460
|
+
| `source_type` | sigma, splunk_escu, elastic, kql, sublime, crowdstrike_cql |
|
|
431
461
|
| `threat_profile` | ransomware, apt, initial-access, persistence, etc. |
|
|
432
462
|
|
|
433
463
|
This prevents typos and helps discover what values are available in your detection corpus.
|
|
@@ -486,7 +516,7 @@ Tool: prioritize_gaps(threat_profile="ransomware")
|
|
|
486
516
|
| `search(query, limit)` | Full-text search across all detection fields (names, descriptions, queries, CVEs, process names, etc.) |
|
|
487
517
|
| `get_by_id(id)` | Get a single detection by its ID |
|
|
488
518
|
| `list_all(limit, offset)` | Paginated list of all detections |
|
|
489
|
-
| `list_by_source(source_type)` | Filter by `sigma`, `splunk_escu`, `elastic`, or `
|
|
519
|
+
| `list_by_source(source_type)` | Filter by `sigma`, `splunk_escu`, `elastic`, `kql`, `sublime`, or `crowdstrike_cql` |
|
|
490
520
|
| `get_raw_yaml(id)` | Get the original YAML/TOML/Markdown content |
|
|
491
521
|
| `get_stats()` | Get index statistics |
|
|
492
522
|
| `rebuild_index()` | Force re-index from configured paths |
|
|
@@ -837,7 +867,7 @@ Tool: list_by_cve(cve_id="CVE-2024-27198")
|
|
|
837
867
|
```
|
|
838
868
|
LLM: "What detections do we have for credential dumping?"
|
|
839
869
|
Tool: search(query="credential dumping", limit=10)
|
|
840
|
-
→ Returns results from Sigma, Splunk, Elastic, AND
|
|
870
|
+
→ Returns results from Sigma, Splunk, Elastic, KQL, Sublime, AND CrowdStrike CQL
|
|
841
871
|
```
|
|
842
872
|
|
|
843
873
|
#### Find Web Server Attack Detections
|
|
@@ -862,6 +892,22 @@ LLM: "What KQL queries do we have for Defender For Endpoint?"
|
|
|
862
892
|
Tool: list_by_kql_category(category="Defender For Endpoint")
|
|
863
893
|
```
|
|
864
894
|
|
|
895
|
+
#### Find BEC/Phishing Email Detections
|
|
896
|
+
|
|
897
|
+
```
|
|
898
|
+
LLM: "What email detections do we have for BEC fraud?"
|
|
899
|
+
Tool: list_by_source(source_type="sublime")
|
|
900
|
+
→ Returns Sublime Security email detection rules for BEC, phishing, malware, etc.
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
#### Find CrowdStrike CQL Hunting Queries
|
|
904
|
+
|
|
905
|
+
```
|
|
906
|
+
LLM: "What CrowdStrike queries do we have for lateral movement?"
|
|
907
|
+
Tool: list_by_source(source_type="crowdstrike_cql")
|
|
908
|
+
→ Returns CQL Hub queries for CrowdStrike NextGen SIEM and Falcon LogScale
|
|
909
|
+
```
|
|
910
|
+
|
|
865
911
|
#### Search for BloodHound Detections
|
|
866
912
|
|
|
867
913
|
```
|
|
@@ -872,7 +918,7 @@ Tool: search(query="bloodhound", limit=10)
|
|
|
872
918
|
|
|
873
919
|
## Unified Schema
|
|
874
920
|
|
|
875
|
-
All detection sources (Sigma, Splunk, Elastic, KQL) are normalized to a common schema:
|
|
921
|
+
All detection sources (Sigma, Splunk, Elastic, KQL, Sublime, CrowdStrike CQL) are normalized to a common schema:
|
|
876
922
|
|
|
877
923
|
### Core Fields
|
|
878
924
|
|
|
@@ -881,8 +927,8 @@ All detection sources (Sigma, Splunk, Elastic, KQL) are normalized to a common s
|
|
|
881
927
|
| `id` | Unique identifier |
|
|
882
928
|
| `name` | Detection name/title |
|
|
883
929
|
| `description` | What the detection looks for |
|
|
884
|
-
| `query` | Detection logic (Sigma YAML, Splunk SPL, Elastic EQL, or
|
|
885
|
-
| `source_type` | `sigma`, `splunk_escu`, `elastic`, or `
|
|
930
|
+
| `query` | Detection logic (Sigma YAML, Splunk SPL, Elastic EQL, KQL, Sublime MQL, or CrowdStrike CQL) |
|
|
931
|
+
| `source_type` | `sigma`, `splunk_escu`, `elastic`, `kql`, `sublime`, or `crowdstrike_cql` |
|
|
886
932
|
| `severity` | Detection severity level |
|
|
887
933
|
| `status` | Rule status (stable, test, experimental, production, etc.) |
|
|
888
934
|
| `author` | Rule author |
|
|
@@ -914,6 +960,14 @@ All detection sources (Sigma, Splunk, Elastic, KQL) are normalized to a common s
|
|
|
914
960
|
| `kql_keywords` | Security keywords extracted for search |
|
|
915
961
|
| `platforms` | Platforms (windows, azure-ad, office-365, etc.) |
|
|
916
962
|
|
|
963
|
+
### Sublime-Specific Fields
|
|
964
|
+
|
|
965
|
+
| Field | Description |
|
|
966
|
+
|-------|-------------|
|
|
967
|
+
| `sublime_attack_types` | Attack types (BEC/Fraud, Credential Phishing, Malware/Ransomware, etc.) |
|
|
968
|
+
| `sublime_detection_methods` | Detection methods (Content analysis, URL analysis, Computer Vision, etc.) |
|
|
969
|
+
| `sublime_tactics` | Tactics and techniques (Evasion, Impersonation: Brand, Social engineering, etc.) |
|
|
970
|
+
|
|
917
971
|
## Database
|
|
918
972
|
|
|
919
973
|
The index is stored at `~/.cache/security-detections-mcp/detections.sqlite`.
|
|
@@ -951,6 +1005,22 @@ From [Elastic Detection Rules](https://github.com/elastic/detection-rules):
|
|
|
951
1005
|
- Optional: `rule.description`, `rule.query`, `rule.severity`, `rule.tags`, `rule.threat` (MITRE mappings)
|
|
952
1006
|
- Supports EQL, KQL, Lucene, and ESQL query languages
|
|
953
1007
|
|
|
1008
|
+
### Sublime Security Rules (YAML)
|
|
1009
|
+
|
|
1010
|
+
From [Sublime Security](https://github.com/sublime-security/sublime-rules):
|
|
1011
|
+
- Required: `name`, `type` (rule/exclusion), `source` (MQL query)
|
|
1012
|
+
- Optional: `description`, `severity`, `id`, `references`, `tags`, `authors`, `attack_types`, `tactics_and_techniques`, `detection_methods`, `false_positives`
|
|
1013
|
+
- Uses MQL (Message Query Language) for email-specific detection logic
|
|
1014
|
+
- Covers BEC/fraud, credential phishing, malware delivery, spam, and more
|
|
1015
|
+
|
|
1016
|
+
### CrowdStrike CQL Queries (YAML)
|
|
1017
|
+
|
|
1018
|
+
From [CQL Hub](https://github.com/ByteRay-Labs/Query-Hub):
|
|
1019
|
+
- Required: `name`, `cql` (CrowdStrike Query Language query)
|
|
1020
|
+
- Optional: `description`, `mitre_ids`, `author`, `log_sources`, `tags`, `cs_required_modules`, `explanation`
|
|
1021
|
+
- Community-driven detection and hunting queries for CrowdStrike NextGen SIEM and Falcon LogScale
|
|
1022
|
+
- Covers endpoint, network, cloud, and identity detection use cases
|
|
1023
|
+
|
|
954
1024
|
### KQL Hunting Queries (Markdown & Raw .kql)
|
|
955
1025
|
|
|
956
1026
|
Supports multiple KQL repositories:
|
|
@@ -1035,6 +1105,8 @@ SIGMA_PATHS="./detections/sigma/rules" \
|
|
|
1035
1105
|
SPLUNK_PATHS="./detections/splunk/detections" \
|
|
1036
1106
|
ELASTIC_PATHS="./detections/elastic/rules" \
|
|
1037
1107
|
KQL_PATHS="./detections/kql" \
|
|
1108
|
+
SUBLIME_PATHS="./detections/sublime-rules/detection-rules" \
|
|
1109
|
+
CQL_HUB_PATHS="./detections/cql-hub/queries" \
|
|
1038
1110
|
STORY_PATHS="./detections/splunk/stories" \
|
|
1039
1111
|
npm start
|
|
1040
1112
|
```
|
|
@@ -1049,11 +1121,13 @@ When fully indexed with all sources:
|
|
|
1049
1121
|
| Splunk ESCU | ~2,000+ |
|
|
1050
1122
|
| Elastic Rules | ~1,500+ |
|
|
1051
1123
|
| KQL Queries | ~420+ |
|
|
1124
|
+
| Sublime Rules | ~900+ |
|
|
1125
|
+
| CrowdStrike CQL | ~139+ |
|
|
1052
1126
|
| Analytic Stories | ~330 |
|
|
1053
|
-
| **Total Detections** | **~
|
|
1127
|
+
| **Total Detections** | **~8,200+** |
|
|
1054
1128
|
| **Indexed Patterns** | **10,235+** |
|
|
1055
1129
|
| **Techniques with Patterns** | **528+** |
|
|
1056
|
-
| **Detection Formats** | **
|
|
1130
|
+
| **Detection Formats** | **6** (Sigma, Splunk, Elastic, KQL, Sublime, CrowdStrike CQL) |
|
|
1057
1131
|
| **Total Tools** | **71+** |
|
|
1058
1132
|
| **MCP Prompts** | **11** |
|
|
1059
1133
|
| **MCP Resources** | **9 static + 5 templates** |
|
|
@@ -1158,7 +1232,7 @@ For detailed information on v2.1 features:
|
|
|
1158
1232
|
|
|
1159
1233
|
| MCP | Purpose |
|
|
1160
1234
|
|-----|---------|
|
|
1161
|
-
| **security-detections-mcp** | Query
|
|
1235
|
+
| **security-detections-mcp** | Query 8,100+ detection rules + 11 expert workflow prompts |
|
|
1162
1236
|
| **mitre-attack-mcp** | ATT&CK framework data, threat groups, Navigator layers |
|
|
1163
1237
|
|
|
1164
1238
|
### With MCP Prompts (Easiest)
|
|
@@ -1224,7 +1298,9 @@ LLM workflow:
|
|
|
1224
1298
|
"SIGMA_PATHS": "/path/to/sigma/rules",
|
|
1225
1299
|
"SPLUNK_PATHS": "/path/to/security_content/detections",
|
|
1226
1300
|
"ELASTIC_PATHS": "/path/to/detection-rules/rules",
|
|
1227
|
-
"KQL_PATHS": "/path/to/kql-hunting-queries"
|
|
1301
|
+
"KQL_PATHS": "/path/to/kql-hunting-queries",
|
|
1302
|
+
"SUBLIME_PATHS": "/path/to/sublime-rules/detection-rules",
|
|
1303
|
+
"CQL_HUB_PATHS": "/path/to/cql-hub/queries"
|
|
1228
1304
|
}
|
|
1229
1305
|
},
|
|
1230
1306
|
"mitre-attack": {
|
package/dist/db/detections.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface ValidationResult {
|
|
|
12
12
|
similar?: string[];
|
|
13
13
|
}
|
|
14
14
|
export interface TechniqueIdFilters {
|
|
15
|
-
source_type?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql';
|
|
15
|
+
source_type?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql';
|
|
16
16
|
tactic?: string;
|
|
17
17
|
severity?: string;
|
|
18
18
|
}
|
|
@@ -59,7 +59,7 @@ export interface DetectionSuggestion {
|
|
|
59
59
|
export interface NavigatorLayerOptions {
|
|
60
60
|
name: string;
|
|
61
61
|
description?: string;
|
|
62
|
-
source_type?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql';
|
|
62
|
+
source_type?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql';
|
|
63
63
|
tactic?: string;
|
|
64
64
|
severity?: string;
|
|
65
65
|
}
|
|
@@ -107,7 +107,7 @@ export declare function listDetections(limit?: number, offset?: number): Detecti
|
|
|
107
107
|
/**
|
|
108
108
|
* List detections filtered by source type.
|
|
109
109
|
*/
|
|
110
|
-
export declare function listBySource(sourceType: 'sigma' | 'splunk_escu' | 'elastic' | 'kql', limit?: number, offset?: number): Detection[];
|
|
110
|
+
export declare function listBySource(sourceType: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql', limit?: number, offset?: number): Detection[];
|
|
111
111
|
/**
|
|
112
112
|
* List detections by MITRE technique ID.
|
|
113
113
|
*/
|
|
@@ -183,15 +183,15 @@ export declare function getTechniqueIds(filters?: TechniqueIdFilters): string[];
|
|
|
183
183
|
/**
|
|
184
184
|
* Analyze coverage by tactic and identify strengths/weaknesses.
|
|
185
185
|
*/
|
|
186
|
-
export declare function analyzeCoverage(sourceType?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql'): CoverageReport;
|
|
186
|
+
export declare function analyzeCoverage(sourceType?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql'): CoverageReport;
|
|
187
187
|
/**
|
|
188
188
|
* Identify gaps based on a threat profile.
|
|
189
189
|
*/
|
|
190
|
-
export declare function identifyGaps(threatProfile: string, sourceType?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql'): GapAnalysis;
|
|
190
|
+
export declare function identifyGaps(threatProfile: string, sourceType?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql'): GapAnalysis;
|
|
191
191
|
/**
|
|
192
192
|
* Suggest detections for a technique.
|
|
193
193
|
*/
|
|
194
|
-
export declare function suggestDetections(techniqueId: string, sourceType?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql'): DetectionSuggestion;
|
|
194
|
+
export declare function suggestDetections(techniqueId: string, sourceType?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql'): DetectionSuggestion;
|
|
195
195
|
/**
|
|
196
196
|
* Generate an ATT&CK Navigator layer from detection coverage.
|
|
197
197
|
*/
|
|
@@ -203,7 +203,7 @@ export declare function searchDetectionList(query: string, limit?: number): Dete
|
|
|
203
203
|
/**
|
|
204
204
|
* List detections by source with optional name filter, returning lightweight results.
|
|
205
205
|
*/
|
|
206
|
-
export declare function listDetectionsBySourceLight(sourceType: 'sigma' | 'splunk_escu' | 'elastic' | 'kql', nameFilter?: string, limit?: number): DetectionListItem[];
|
|
206
|
+
export declare function listDetectionsBySourceLight(sourceType: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql', nameFilter?: string, limit?: number): DetectionListItem[];
|
|
207
207
|
/**
|
|
208
208
|
* Compare detections across sources for a topic.
|
|
209
209
|
*/
|
|
@@ -211,7 +211,7 @@ export declare function compareDetectionsBySource(topic: string, limit?: number)
|
|
|
211
211
|
/**
|
|
212
212
|
* Get detection names and IDs matching a pattern, grouped by source.
|
|
213
213
|
*/
|
|
214
|
-
export declare function getDetectionNamesByPattern(pattern: string, sourceType?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql'): {
|
|
214
|
+
export declare function getDetectionNamesByPattern(pattern: string, sourceType?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql'): {
|
|
215
215
|
source: string;
|
|
216
216
|
detections: Array<{
|
|
217
217
|
name: string;
|
package/dist/db/detections.js
CHANGED
|
@@ -75,6 +75,9 @@ function rowToDetection(row) {
|
|
|
75
75
|
kql_category: row.kql_category,
|
|
76
76
|
kql_tags: safeJsonParse(row.kql_tags, []),
|
|
77
77
|
kql_keywords: safeJsonParse(row.kql_keywords, []),
|
|
78
|
+
sublime_attack_types: safeJsonParse(row.sublime_attack_types, []),
|
|
79
|
+
sublime_detection_methods: safeJsonParse(row.sublime_detection_methods, []),
|
|
80
|
+
sublime_tactics: safeJsonParse(row.sublime_tactics, []),
|
|
78
81
|
};
|
|
79
82
|
}
|
|
80
83
|
function rowToListItem(row) {
|
|
@@ -95,15 +98,16 @@ function rowToListItem(row) {
|
|
|
95
98
|
export function insertDetection(detection) {
|
|
96
99
|
const database = getDb();
|
|
97
100
|
const stmt = database.prepare(`
|
|
98
|
-
INSERT OR REPLACE INTO detections
|
|
99
|
-
(id, name, description, query, source_type, mitre_ids, logsource_category,
|
|
100
|
-
logsource_product, logsource_service, severity, status, author,
|
|
101
|
+
INSERT OR REPLACE INTO detections
|
|
102
|
+
(id, name, description, query, source_type, mitre_ids, logsource_category,
|
|
103
|
+
logsource_product, logsource_service, severity, status, author,
|
|
101
104
|
date_created, date_modified, refs, falsepositives, tags, file_path, raw_yaml,
|
|
102
105
|
cves, analytic_stories, data_sources, detection_type, asset_type, security_domain,
|
|
103
|
-
process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords
|
|
104
|
-
|
|
106
|
+
process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords,
|
|
107
|
+
sublime_attack_types, sublime_detection_methods, sublime_tactics)
|
|
108
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
105
109
|
`);
|
|
106
|
-
stmt.run(detection.id, detection.name, detection.description, detection.query, detection.source_type, JSON.stringify(detection.mitre_ids), detection.logsource_category, detection.logsource_product, detection.logsource_service, detection.severity, detection.status, detection.author, detection.date_created, detection.date_modified, JSON.stringify(detection.references), JSON.stringify(detection.falsepositives), JSON.stringify(detection.tags), detection.file_path, detection.raw_yaml, JSON.stringify(detection.cves), JSON.stringify(detection.analytic_stories), JSON.stringify(detection.data_sources), detection.detection_type, detection.asset_type, detection.security_domain, JSON.stringify(detection.process_names), JSON.stringify(detection.file_paths), JSON.stringify(detection.registry_paths), JSON.stringify(detection.mitre_tactics), JSON.stringify(detection.platforms), detection.kql_category, JSON.stringify(detection.kql_tags), JSON.stringify(detection.kql_keywords));
|
|
110
|
+
stmt.run(detection.id, detection.name, detection.description, detection.query, detection.source_type, JSON.stringify(detection.mitre_ids), detection.logsource_category, detection.logsource_product, detection.logsource_service, detection.severity, detection.status, detection.author, detection.date_created, detection.date_modified, JSON.stringify(detection.references), JSON.stringify(detection.falsepositives), JSON.stringify(detection.tags), detection.file_path, detection.raw_yaml, JSON.stringify(detection.cves), JSON.stringify(detection.analytic_stories), JSON.stringify(detection.data_sources), detection.detection_type, detection.asset_type, detection.security_domain, JSON.stringify(detection.process_names), JSON.stringify(detection.file_paths), JSON.stringify(detection.registry_paths), JSON.stringify(detection.mitre_tactics), JSON.stringify(detection.platforms), detection.kql_category, JSON.stringify(detection.kql_tags), JSON.stringify(detection.kql_keywords), JSON.stringify(detection.sublime_attack_types), JSON.stringify(detection.sublime_detection_methods), JSON.stringify(detection.sublime_tactics));
|
|
107
111
|
}
|
|
108
112
|
/**
|
|
109
113
|
* Get a detection by its ID.
|
|
@@ -348,6 +352,7 @@ export function getStats() {
|
|
|
348
352
|
const splunk = database.prepare("SELECT COUNT(*) as count FROM detections WHERE source_type = 'splunk_escu'").get().count;
|
|
349
353
|
const elastic = database.prepare("SELECT COUNT(*) as count FROM detections WHERE source_type = 'elastic'").get().count;
|
|
350
354
|
const kql = database.prepare("SELECT COUNT(*) as count FROM detections WHERE source_type = 'kql'").get().count;
|
|
355
|
+
const sublime = database.prepare("SELECT COUNT(*) as count FROM detections WHERE source_type = 'sublime'").get().count;
|
|
351
356
|
// Count by severity
|
|
352
357
|
const severityRows = database.prepare(`
|
|
353
358
|
SELECT severity, COUNT(*) as count FROM detections
|
|
@@ -425,6 +430,7 @@ export function getStats() {
|
|
|
425
430
|
splunk_escu: splunk,
|
|
426
431
|
elastic,
|
|
427
432
|
kql,
|
|
433
|
+
sublime,
|
|
428
434
|
by_severity,
|
|
429
435
|
by_logsource_product,
|
|
430
436
|
mitre_coverage,
|
|
@@ -863,6 +869,7 @@ export function compareDetectionsBySource(topic, limit = 100) {
|
|
|
863
869
|
splunk_escu: [],
|
|
864
870
|
elastic: [],
|
|
865
871
|
kql: [],
|
|
872
|
+
sublime: [],
|
|
866
873
|
};
|
|
867
874
|
const byTactic = {};
|
|
868
875
|
for (const row of rows) {
|
|
@@ -936,7 +943,7 @@ export function countDetectionsBySource(topic) {
|
|
|
936
943
|
GROUP BY d.source_type
|
|
937
944
|
`);
|
|
938
945
|
const rows = stmt.all(topic);
|
|
939
|
-
const result = { sigma: 0, splunk_escu: 0, elastic: 0, kql: 0 };
|
|
946
|
+
const result = { sigma: 0, splunk_escu: 0, elastic: 0, kql: 0, sublime: 0, crowdstrike_cql: 0 };
|
|
940
947
|
for (const row of rows) {
|
|
941
948
|
result[row.source_type] = row.count;
|
|
942
949
|
}
|
package/dist/db/schema.js
CHANGED
|
@@ -56,7 +56,10 @@ function createDetectionsTable(db) {
|
|
|
56
56
|
platforms TEXT,
|
|
57
57
|
kql_category TEXT,
|
|
58
58
|
kql_tags TEXT,
|
|
59
|
-
kql_keywords TEXT
|
|
59
|
+
kql_keywords TEXT,
|
|
60
|
+
sublime_attack_types TEXT,
|
|
61
|
+
sublime_detection_methods TEXT,
|
|
62
|
+
sublime_tactics TEXT
|
|
60
63
|
)
|
|
61
64
|
`);
|
|
62
65
|
}
|
|
@@ -83,6 +86,9 @@ function createDetectionsFts(db) {
|
|
|
83
86
|
kql_category,
|
|
84
87
|
kql_tags,
|
|
85
88
|
kql_keywords,
|
|
89
|
+
sublime_attack_types,
|
|
90
|
+
sublime_detection_methods,
|
|
91
|
+
sublime_tactics,
|
|
86
92
|
content='detections',
|
|
87
93
|
content_rowid='rowid'
|
|
88
94
|
)
|
|
@@ -95,24 +101,24 @@ function createDetectionsTriggers(db) {
|
|
|
95
101
|
// After INSERT trigger
|
|
96
102
|
db.exec(`
|
|
97
103
|
CREATE TRIGGER IF NOT EXISTS detections_ai AFTER INSERT ON detections BEGIN
|
|
98
|
-
INSERT INTO detections_fts(rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords)
|
|
99
|
-
VALUES (NEW.rowid, NEW.id, NEW.name, NEW.description, NEW.query, NEW.mitre_ids, NEW.tags, NEW.cves, NEW.analytic_stories, NEW.data_sources, NEW.process_names, NEW.file_paths, NEW.registry_paths, NEW.mitre_tactics, NEW.platforms, NEW.kql_category, NEW.kql_tags, NEW.kql_keywords);
|
|
104
|
+
INSERT INTO detections_fts(rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords, sublime_attack_types, sublime_detection_methods, sublime_tactics)
|
|
105
|
+
VALUES (NEW.rowid, NEW.id, NEW.name, NEW.description, NEW.query, NEW.mitre_ids, NEW.tags, NEW.cves, NEW.analytic_stories, NEW.data_sources, NEW.process_names, NEW.file_paths, NEW.registry_paths, NEW.mitre_tactics, NEW.platforms, NEW.kql_category, NEW.kql_tags, NEW.kql_keywords, NEW.sublime_attack_types, NEW.sublime_detection_methods, NEW.sublime_tactics);
|
|
100
106
|
END
|
|
101
107
|
`);
|
|
102
108
|
// After DELETE trigger
|
|
103
109
|
db.exec(`
|
|
104
110
|
CREATE TRIGGER IF NOT EXISTS detections_ad AFTER DELETE ON detections BEGIN
|
|
105
|
-
INSERT INTO detections_fts(detections_fts, rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords)
|
|
106
|
-
VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.description, OLD.query, OLD.mitre_ids, OLD.tags, OLD.cves, OLD.analytic_stories, OLD.data_sources, OLD.process_names, OLD.file_paths, OLD.registry_paths, OLD.mitre_tactics, OLD.platforms, OLD.kql_category, OLD.kql_tags, OLD.kql_keywords);
|
|
111
|
+
INSERT INTO detections_fts(detections_fts, rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords, sublime_attack_types, sublime_detection_methods, sublime_tactics)
|
|
112
|
+
VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.description, OLD.query, OLD.mitre_ids, OLD.tags, OLD.cves, OLD.analytic_stories, OLD.data_sources, OLD.process_names, OLD.file_paths, OLD.registry_paths, OLD.mitre_tactics, OLD.platforms, OLD.kql_category, OLD.kql_tags, OLD.kql_keywords, OLD.sublime_attack_types, OLD.sublime_detection_methods, OLD.sublime_tactics);
|
|
107
113
|
END
|
|
108
114
|
`);
|
|
109
115
|
// After UPDATE trigger
|
|
110
116
|
db.exec(`
|
|
111
117
|
CREATE TRIGGER IF NOT EXISTS detections_au AFTER UPDATE ON detections BEGIN
|
|
112
|
-
INSERT INTO detections_fts(detections_fts, rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords)
|
|
113
|
-
VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.description, OLD.query, OLD.mitre_ids, OLD.tags, OLD.cves, OLD.analytic_stories, OLD.data_sources, OLD.process_names, OLD.file_paths, OLD.registry_paths, OLD.mitre_tactics, OLD.platforms, OLD.kql_category, OLD.kql_tags, OLD.kql_keywords);
|
|
114
|
-
INSERT INTO detections_fts(rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords)
|
|
115
|
-
VALUES (NEW.rowid, NEW.id, NEW.name, NEW.description, NEW.query, NEW.mitre_ids, NEW.tags, NEW.cves, NEW.analytic_stories, NEW.data_sources, NEW.process_names, NEW.file_paths, NEW.registry_paths, NEW.mitre_tactics, NEW.platforms, NEW.kql_category, NEW.kql_tags, NEW.kql_keywords);
|
|
118
|
+
INSERT INTO detections_fts(detections_fts, rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords, sublime_attack_types, sublime_detection_methods, sublime_tactics)
|
|
119
|
+
VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.description, OLD.query, OLD.mitre_ids, OLD.tags, OLD.cves, OLD.analytic_stories, OLD.data_sources, OLD.process_names, OLD.file_paths, OLD.registry_paths, OLD.mitre_tactics, OLD.platforms, OLD.kql_category, OLD.kql_tags, OLD.kql_keywords, OLD.sublime_attack_types, OLD.sublime_detection_methods, OLD.sublime_tactics);
|
|
120
|
+
INSERT INTO detections_fts(rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords, sublime_attack_types, sublime_detection_methods, sublime_tactics)
|
|
121
|
+
VALUES (NEW.rowid, NEW.id, NEW.name, NEW.description, NEW.query, NEW.mitre_ids, NEW.tags, NEW.cves, NEW.analytic_stories, NEW.data_sources, NEW.process_names, NEW.file_paths, NEW.registry_paths, NEW.mitre_tactics, NEW.platforms, NEW.kql_category, NEW.kql_tags, NEW.kql_keywords, NEW.sublime_attack_types, NEW.sublime_detection_methods, NEW.sublime_tactics);
|
|
116
122
|
END
|
|
117
123
|
`);
|
|
118
124
|
}
|
package/dist/db.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare function insertDetection(detection: Detection): void;
|
|
|
8
8
|
export declare function searchDetections(query: string, limit?: number): Detection[];
|
|
9
9
|
export declare function getDetectionById(id: string): Detection | null;
|
|
10
10
|
export declare function listDetections(limit?: number, offset?: number): Detection[];
|
|
11
|
-
export declare function listBySource(sourceType: 'sigma' | 'splunk_escu' | 'elastic' | 'kql', limit?: number, offset?: number): Detection[];
|
|
11
|
+
export declare function listBySource(sourceType: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql', limit?: number, offset?: number): Detection[];
|
|
12
12
|
export declare function listByMitre(techniqueId: string, limit?: number, offset?: number): Detection[];
|
|
13
13
|
export declare function listByLogsource(category?: string, product?: string, service?: string, limit?: number, offset?: number): Detection[];
|
|
14
14
|
export declare function listBySeverity(level: string, limit?: number, offset?: number): Detection[];
|
|
@@ -117,9 +117,9 @@ export interface SourceComparisonResult {
|
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
export declare function searchDetectionList(query: string, limit?: number): DetectionListItem[];
|
|
120
|
-
export declare function listDetectionsBySourceLight(sourceType: 'sigma' | 'splunk_escu' | 'elastic' | 'kql', nameFilter?: string, limit?: number): DetectionListItem[];
|
|
120
|
+
export declare function listDetectionsBySourceLight(sourceType: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql', nameFilter?: string, limit?: number): DetectionListItem[];
|
|
121
121
|
export declare function compareDetectionsBySource(topic: string, limit?: number): SourceComparisonResult;
|
|
122
|
-
export declare function getDetectionNamesByPattern(pattern: string, sourceType?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql'): {
|
|
122
|
+
export declare function getDetectionNamesByPattern(pattern: string, sourceType?: 'sigma' | 'splunk_escu' | 'elastic' | 'kql' | 'sublime' | 'crowdstrike_cql'): {
|
|
123
123
|
source: string;
|
|
124
124
|
detections: Array<{
|
|
125
125
|
name: string;
|
package/dist/db.js
CHANGED
|
@@ -51,7 +51,10 @@ export function initDb() {
|
|
|
51
51
|
platforms TEXT,
|
|
52
52
|
kql_category TEXT,
|
|
53
53
|
kql_tags TEXT,
|
|
54
|
-
kql_keywords TEXT
|
|
54
|
+
kql_keywords TEXT,
|
|
55
|
+
sublime_attack_types TEXT,
|
|
56
|
+
sublime_detection_methods TEXT,
|
|
57
|
+
sublime_tactics TEXT
|
|
55
58
|
)
|
|
56
59
|
`);
|
|
57
60
|
// Create FTS5 virtual table for full-text search with all searchable fields
|
|
@@ -74,6 +77,9 @@ export function initDb() {
|
|
|
74
77
|
kql_category,
|
|
75
78
|
kql_tags,
|
|
76
79
|
kql_keywords,
|
|
80
|
+
sublime_attack_types,
|
|
81
|
+
sublime_detection_methods,
|
|
82
|
+
sublime_tactics,
|
|
77
83
|
content='detections',
|
|
78
84
|
content_rowid='rowid'
|
|
79
85
|
)
|
|
@@ -81,22 +87,22 @@ export function initDb() {
|
|
|
81
87
|
// Create triggers to keep FTS in sync
|
|
82
88
|
db.exec(`
|
|
83
89
|
CREATE TRIGGER IF NOT EXISTS detections_ai AFTER INSERT ON detections BEGIN
|
|
84
|
-
INSERT INTO detections_fts(rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords)
|
|
85
|
-
VALUES (NEW.rowid, NEW.id, NEW.name, NEW.description, NEW.query, NEW.mitre_ids, NEW.tags, NEW.cves, NEW.analytic_stories, NEW.data_sources, NEW.process_names, NEW.file_paths, NEW.registry_paths, NEW.mitre_tactics, NEW.platforms, NEW.kql_category, NEW.kql_tags, NEW.kql_keywords);
|
|
90
|
+
INSERT INTO detections_fts(rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords, sublime_attack_types, sublime_detection_methods, sublime_tactics)
|
|
91
|
+
VALUES (NEW.rowid, NEW.id, NEW.name, NEW.description, NEW.query, NEW.mitre_ids, NEW.tags, NEW.cves, NEW.analytic_stories, NEW.data_sources, NEW.process_names, NEW.file_paths, NEW.registry_paths, NEW.mitre_tactics, NEW.platforms, NEW.kql_category, NEW.kql_tags, NEW.kql_keywords, NEW.sublime_attack_types, NEW.sublime_detection_methods, NEW.sublime_tactics);
|
|
86
92
|
END
|
|
87
93
|
`);
|
|
88
94
|
db.exec(`
|
|
89
95
|
CREATE TRIGGER IF NOT EXISTS detections_ad AFTER DELETE ON detections BEGIN
|
|
90
|
-
INSERT INTO detections_fts(detections_fts, rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords)
|
|
91
|
-
VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.description, OLD.query, OLD.mitre_ids, OLD.tags, OLD.cves, OLD.analytic_stories, OLD.data_sources, OLD.process_names, OLD.file_paths, OLD.registry_paths, OLD.mitre_tactics, OLD.platforms, OLD.kql_category, OLD.kql_tags, OLD.kql_keywords);
|
|
96
|
+
INSERT INTO detections_fts(detections_fts, rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords, sublime_attack_types, sublime_detection_methods, sublime_tactics)
|
|
97
|
+
VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.description, OLD.query, OLD.mitre_ids, OLD.tags, OLD.cves, OLD.analytic_stories, OLD.data_sources, OLD.process_names, OLD.file_paths, OLD.registry_paths, OLD.mitre_tactics, OLD.platforms, OLD.kql_category, OLD.kql_tags, OLD.kql_keywords, OLD.sublime_attack_types, OLD.sublime_detection_methods, OLD.sublime_tactics);
|
|
92
98
|
END
|
|
93
99
|
`);
|
|
94
100
|
db.exec(`
|
|
95
101
|
CREATE TRIGGER IF NOT EXISTS detections_au AFTER UPDATE ON detections BEGIN
|
|
96
|
-
INSERT INTO detections_fts(detections_fts, rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords)
|
|
97
|
-
VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.description, OLD.query, OLD.mitre_ids, OLD.tags, OLD.cves, OLD.analytic_stories, OLD.data_sources, OLD.process_names, OLD.file_paths, OLD.registry_paths, OLD.mitre_tactics, OLD.platforms, OLD.kql_category, OLD.kql_tags, OLD.kql_keywords);
|
|
98
|
-
INSERT INTO detections_fts(rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords)
|
|
99
|
-
VALUES (NEW.rowid, NEW.id, NEW.name, NEW.description, NEW.query, NEW.mitre_ids, NEW.tags, NEW.cves, NEW.analytic_stories, NEW.data_sources, NEW.process_names, NEW.file_paths, NEW.registry_paths, NEW.mitre_tactics, NEW.platforms, NEW.kql_category, NEW.kql_tags, NEW.kql_keywords);
|
|
102
|
+
INSERT INTO detections_fts(detections_fts, rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords, sublime_attack_types, sublime_detection_methods, sublime_tactics)
|
|
103
|
+
VALUES ('delete', OLD.rowid, OLD.id, OLD.name, OLD.description, OLD.query, OLD.mitre_ids, OLD.tags, OLD.cves, OLD.analytic_stories, OLD.data_sources, OLD.process_names, OLD.file_paths, OLD.registry_paths, OLD.mitre_tactics, OLD.platforms, OLD.kql_category, OLD.kql_tags, OLD.kql_keywords, OLD.sublime_attack_types, OLD.sublime_detection_methods, OLD.sublime_tactics);
|
|
104
|
+
INSERT INTO detections_fts(rowid, id, name, description, query, mitre_ids, tags, cves, analytic_stories, data_sources, process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords, sublime_attack_types, sublime_detection_methods, sublime_tactics)
|
|
105
|
+
VALUES (NEW.rowid, NEW.id, NEW.name, NEW.description, NEW.query, NEW.mitre_ids, NEW.tags, NEW.cves, NEW.analytic_stories, NEW.data_sources, NEW.process_names, NEW.file_paths, NEW.registry_paths, NEW.mitre_tactics, NEW.platforms, NEW.kql_category, NEW.kql_tags, NEW.kql_keywords, NEW.sublime_attack_types, NEW.sublime_detection_methods, NEW.sublime_tactics);
|
|
100
106
|
END
|
|
101
107
|
`);
|
|
102
108
|
// Create indexes for common queries
|
|
@@ -199,15 +205,16 @@ export function recreateDb() {
|
|
|
199
205
|
export function insertDetection(detection) {
|
|
200
206
|
const database = initDb();
|
|
201
207
|
const stmt = database.prepare(`
|
|
202
|
-
INSERT OR REPLACE INTO detections
|
|
203
|
-
(id, name, description, query, source_type, mitre_ids, logsource_category,
|
|
204
|
-
logsource_product, logsource_service, severity, status, author,
|
|
208
|
+
INSERT OR REPLACE INTO detections
|
|
209
|
+
(id, name, description, query, source_type, mitre_ids, logsource_category,
|
|
210
|
+
logsource_product, logsource_service, severity, status, author,
|
|
205
211
|
date_created, date_modified, refs, falsepositives, tags, file_path, raw_yaml,
|
|
206
212
|
cves, analytic_stories, data_sources, detection_type, asset_type, security_domain,
|
|
207
|
-
process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords
|
|
208
|
-
|
|
213
|
+
process_names, file_paths, registry_paths, mitre_tactics, platforms, kql_category, kql_tags, kql_keywords,
|
|
214
|
+
sublime_attack_types, sublime_detection_methods, sublime_tactics)
|
|
215
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
209
216
|
`);
|
|
210
|
-
stmt.run(detection.id, detection.name, detection.description, detection.query, detection.source_type, JSON.stringify(detection.mitre_ids), detection.logsource_category, detection.logsource_product, detection.logsource_service, detection.severity, detection.status, detection.author, detection.date_created, detection.date_modified, JSON.stringify(detection.references), JSON.stringify(detection.falsepositives), JSON.stringify(detection.tags), detection.file_path, detection.raw_yaml, JSON.stringify(detection.cves), JSON.stringify(detection.analytic_stories), JSON.stringify(detection.data_sources), detection.detection_type, detection.asset_type, detection.security_domain, JSON.stringify(detection.process_names), JSON.stringify(detection.file_paths), JSON.stringify(detection.registry_paths), JSON.stringify(detection.mitre_tactics), JSON.stringify(detection.platforms), detection.kql_category, JSON.stringify(detection.kql_tags), JSON.stringify(detection.kql_keywords));
|
|
217
|
+
stmt.run(detection.id, detection.name, detection.description, detection.query, detection.source_type, JSON.stringify(detection.mitre_ids), detection.logsource_category, detection.logsource_product, detection.logsource_service, detection.severity, detection.status, detection.author, detection.date_created, detection.date_modified, JSON.stringify(detection.references), JSON.stringify(detection.falsepositives), JSON.stringify(detection.tags), detection.file_path, detection.raw_yaml, JSON.stringify(detection.cves), JSON.stringify(detection.analytic_stories), JSON.stringify(detection.data_sources), detection.detection_type, detection.asset_type, detection.security_domain, JSON.stringify(detection.process_names), JSON.stringify(detection.file_paths), JSON.stringify(detection.registry_paths), JSON.stringify(detection.mitre_tactics), JSON.stringify(detection.platforms), detection.kql_category, JSON.stringify(detection.kql_tags), JSON.stringify(detection.kql_keywords), JSON.stringify(detection.sublime_attack_types), JSON.stringify(detection.sublime_detection_methods), JSON.stringify(detection.sublime_tactics));
|
|
211
218
|
}
|
|
212
219
|
function rowToDetection(row) {
|
|
213
220
|
return {
|
|
@@ -244,6 +251,9 @@ function rowToDetection(row) {
|
|
|
244
251
|
kql_category: row.kql_category,
|
|
245
252
|
kql_tags: JSON.parse(row.kql_tags || '[]'),
|
|
246
253
|
kql_keywords: JSON.parse(row.kql_keywords || '[]'),
|
|
254
|
+
sublime_attack_types: JSON.parse(row.sublime_attack_types || '[]'),
|
|
255
|
+
sublime_detection_methods: JSON.parse(row.sublime_detection_methods || '[]'),
|
|
256
|
+
sublime_tactics: JSON.parse(row.sublime_tactics || '[]'),
|
|
247
257
|
};
|
|
248
258
|
}
|
|
249
259
|
export function searchDetections(query, limit = 50) {
|
|
@@ -419,6 +429,7 @@ export function getStats() {
|
|
|
419
429
|
const splunk = database.prepare("SELECT COUNT(*) as count FROM detections WHERE source_type = 'splunk_escu'").get().count;
|
|
420
430
|
const elastic = database.prepare("SELECT COUNT(*) as count FROM detections WHERE source_type = 'elastic'").get().count;
|
|
421
431
|
const kql = database.prepare("SELECT COUNT(*) as count FROM detections WHERE source_type = 'kql'").get().count;
|
|
432
|
+
const sublime = database.prepare("SELECT COUNT(*) as count FROM detections WHERE source_type = 'sublime'").get().count;
|
|
422
433
|
// Count by severity
|
|
423
434
|
const severityRows = database.prepare(`
|
|
424
435
|
SELECT severity, COUNT(*) as count FROM detections
|
|
@@ -496,6 +507,7 @@ export function getStats() {
|
|
|
496
507
|
splunk_escu: splunk,
|
|
497
508
|
elastic,
|
|
498
509
|
kql,
|
|
510
|
+
sublime,
|
|
499
511
|
by_severity,
|
|
500
512
|
by_logsource_product,
|
|
501
513
|
mitre_coverage,
|
package/dist/index.js
CHANGED
|
@@ -27,17 +27,19 @@ const SPLUNK_PATHS = parsePaths(process.env.SPLUNK_PATHS);
|
|
|
27
27
|
const ELASTIC_PATHS = parsePaths(process.env.ELASTIC_PATHS);
|
|
28
28
|
const STORY_PATHS = parsePaths(process.env.STORY_PATHS);
|
|
29
29
|
const KQL_PATHS = parsePaths(process.env.KQL_PATHS);
|
|
30
|
+
const SUBLIME_PATHS = parsePaths(process.env.SUBLIME_PATHS);
|
|
31
|
+
const CQL_HUB_PATHS = parsePaths(process.env.CQL_HUB_PATHS);
|
|
30
32
|
// Auto-index on startup if paths are configured and DB is empty
|
|
31
33
|
function autoIndex() {
|
|
32
|
-
if (SIGMA_PATHS.length === 0 && SPLUNK_PATHS.length === 0 && ELASTIC_PATHS.length === 0 && KQL_PATHS.length === 0) {
|
|
34
|
+
if (SIGMA_PATHS.length === 0 && SPLUNK_PATHS.length === 0 && ELASTIC_PATHS.length === 0 && KQL_PATHS.length === 0 && SUBLIME_PATHS.length === 0 && CQL_HUB_PATHS.length === 0) {
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
35
37
|
initDb();
|
|
36
38
|
if (needsIndexing()) {
|
|
37
39
|
console.error('[security-detections-mcp] Auto-indexing detections...');
|
|
38
|
-
const result = indexDetections(SIGMA_PATHS, SPLUNK_PATHS, STORY_PATHS, ELASTIC_PATHS, KQL_PATHS);
|
|
40
|
+
const result = indexDetections(SIGMA_PATHS, SPLUNK_PATHS, STORY_PATHS, ELASTIC_PATHS, KQL_PATHS, SUBLIME_PATHS, CQL_HUB_PATHS);
|
|
39
41
|
let msg = `[security-detections-mcp] Indexed ${result.total} detections`;
|
|
40
|
-
msg += ` (${result.sigma_indexed} Sigma, ${result.splunk_indexed} Splunk, ${result.elastic_indexed} Elastic, ${result.kql_indexed} KQL)`;
|
|
42
|
+
msg += ` (${result.sigma_indexed} Sigma, ${result.splunk_indexed} Splunk, ${result.elastic_indexed} Elastic, ${result.kql_indexed} KQL, ${result.sublime_indexed} Sublime, ${result.cql_hub_indexed} CrowdStrike CQL)`;
|
|
41
43
|
if (result.stories_indexed > 0) {
|
|
42
44
|
msg += `, ${result.stories_indexed} stories`;
|
|
43
45
|
}
|