security-detections-mcp 2.1.1 → 3.1.0

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 CHANGED
@@ -1,9 +1,105 @@
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 **KQL** security detection rules.
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
+
5
+ > **New here? Start with the [Setup Guide](./SETUP.md)** -- covers macOS, Windows (WSL & native), and Linux step by step.
6
+
7
+ ## What's New in 3.1 - New Detection Sources
8
+
9
+ - **CrowdStrike CQL Hub** - Query and search CrowdStrike Query Language (CQL) detections from the CQL Hub community repository
10
+ - **Sublime Rules** - Query and search Sublime Security detection rules for email-based threats
11
+
12
+ ## What's New in 3.0 - Autonomous Detection Platform
13
+
14
+ Version 3.0 transforms this MCP into a **fully autonomous detection engineering platform**. Feed it threat intelligence, and it automatically:
15
+
16
+ 1. **Extracts TTPs** from threat reports, CISA alerts, or manual input
17
+ 2. **Analyzes coverage gaps** against your existing detections
18
+ 3. **Generates detections** in your SIEM's native format (SPL, KQL, EQL, or Sigma)
19
+ 4. **Runs Atomic Red Team tests** against your lab environment
20
+ 5. **Validates detections fire** by querying your SIEM
21
+ 6. **Exports attack data** for reproducibility
22
+ 7. **Stages DRAFT PRs** to your detection repo (never auto-merges)
23
+
24
+ > **Multi-SIEM**: Set `SIEM_PLATFORM` to `splunk`, `sentinel`, `elastic`, or `sigma` in your `.env`. The pipeline was built on Splunk + Attack Range but adapts to any SIEM. See the **[E2E Testing Guide](./docs/E2E-TESTING-GUIDE.md)** for complete setup instructions per platform.
25
+
26
+ ### Architecture: LangGraph + Cursor Subagents
27
+
28
+ The 3.0 architecture uses two complementary systems:
29
+
30
+ | Component | Purpose | Location |
31
+ |-----------|---------|----------|
32
+ | **LangGraph Pipeline** | Core autonomous workflow - portable, testable, CI/CD ready | `agents/` |
33
+ | **Cursor Subagents** | Interactive IDE agents for manual tasks | `.cursor/agents/` |
34
+
35
+ ### Quick Start - Autonomous Mode
36
+
37
+ **Prerequisites**: Node.js 20+, an Anthropic API key. Full details in the [Setup Guide](./SETUP.md).
38
+
39
+ ```bash
40
+ # Install the agents package
41
+ cd agents && npm install --registry https://registry.npmjs.org/
42
+
43
+ # Configure
44
+ cp .env.example .env
45
+ # Edit .env: set SIEM_PLATFORM, ANTHROPIC_API_KEY, SECURITY_CONTENT_PATH
46
+
47
+ # Test with dry run first (uses mock data, no LLM calls)
48
+ DRY_RUN=true npm run orchestrate -- --type technique --input "T1566.004 Spearphishing Voice"
49
+
50
+ # Run with real LLM (creates actual detections)
51
+ npm run orchestrate -- --type technique --input "T1566.004 Spearphishing Voice"
52
+
53
+ # Or analyze a CISA alert
54
+ npm run orchestrate -- --type cisa_alert --url https://www.cisa.gov/news-events/alerts/...
55
+
56
+ # Or feed it a threat report
57
+ npm run orchestrate -- --type threat_report --file ./report.md
58
+
59
+ # Note: Use T1566.004 for testing - it has no existing coverage so will create a detection
60
+ # T1003.001 has 100+ existing detections, so the pipeline will correctly skip it (no gap)
61
+ ```
62
+
63
+ ### Pipeline Stages
64
+
65
+ ```
66
+ ┌─────────────┐ ┌──────────────────┐ ┌────────────────────┐
67
+ │ CTI Analyst │───>│ Coverage Analyzer│───>│ Detection Engineer │
68
+ └─────────────┘ └──────────────────┘ └────────────────────┘
69
+
70
+
71
+ ┌───────────┐ ┌──────────────────┐ ┌──────────────────────┐
72
+ │ PR Stager │<───│ Data Dumper │<───│ Splunk Validator │
73
+ └───────────┘ └──────────────────┘ └──────────────────────┘
74
+
75
+
76
+ ┌──────────────────┐
77
+ │ Atomic Executor │
78
+ └──────────────────┘
79
+ ```
80
+
81
+ ### MCP Integration
82
+
83
+ The autonomous pipeline integrates with existing MCPs:
84
+ - **security-detections** - Coverage analysis and gap identification
85
+ - **splunk-mcp** - Detection validation (`run_detection`, `export_dump`)
86
+ - **mitre-attack** - Technique lookups
87
+
88
+ ### Human-in-the-Loop
89
+
90
+ **CRITICAL**: The system NEVER auto-commits or auto-merges. All PRs are created as **DRAFT** requiring human review:
91
+
92
+ ```
93
+ [PR Stager] ✓ security_content DRAFT PR created: https://github.com/splunk/security_content/pull/123
94
+ [PR Stager] ✓ attack_data DRAFT PR created: https://github.com/splunk/attack_data/pull/456
95
+ ```
96
+
97
+ 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).
4
98
 
5
99
  [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=security-detections&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInNlY3VyaXR5LWRldGVjdGlvbnMtbWNwIl0sImVudiI6eyJTSUdNQV9QQVRIUyI6Ii9wYXRoL3RvL3NpZ21hL3J1bGVzLC9wYXRoL3RvL3NpZ21hL3J1bGVzLXRocmVhdC1odW50aW5nIiwiU1BMVU5LX1BBVEhTIjoiL3BhdGgvdG8vc2VjdXJpdHlfY29udGVudC9kZXRlY3Rpb25zIiwiU1RPUllfUEFUSFMiOiIvcGF0aC90by9zZWN1cml0eV9jb250ZW50L3N0b3JpZXMiLCJFTEFTVElDX1BBVEhTIjoiL3BhdGgvdG8vZGV0ZWN0aW9uLXJ1bGVzL3J1bGVzIiwiS1FMX1BBVEhTIjoiL3BhdGgvdG8va3FsLXJ1bGVzIn19)
6
100
 
101
+ > **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.
102
+
7
103
  ## 🐛 Version 2.1.1 (Bug Fix)
8
104
 
9
105
  - **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.
@@ -122,7 +218,7 @@ Claude will:
122
218
  - **🆕 Server Instructions** - Built-in usage guide with examples for better LLM understanding
123
219
  - **🆕 Structured Errors** - Helpful error messages with suggestions and similar items
124
220
  - **🆕 Interactive Tools** - Gap prioritization and sprint planning with form-based input (Cursor 0.42+)
125
- - **Unified Search** - Query Sigma, Splunk ESCU, Elastic, and KQL detections from a single interface
221
+ - **Unified Search** - Query Sigma, Splunk ESCU, Elastic, KQL, Sublime, and CrowdStrike CQL detections from a single interface
126
222
  - **Full-Text Search** - SQLite FTS5 powered search across names, descriptions, queries, MITRE tactics, CVEs, process names, and more
127
223
  - **MITRE ATT&CK Mapping** - Filter detections by technique ID or tactic
128
224
  - **CVE Coverage** - Find detections for specific CVE vulnerabilities
@@ -130,7 +226,7 @@ Claude will:
130
226
  - **Analytic Stories** - Query by Splunk analytic story (optional - enhances context)
131
227
  - **KQL Categories** - Filter KQL queries by category (Defender For Endpoint, Azure AD, Threat Hunting, etc.)
132
228
  - **Auto-Indexing** - Automatically indexes detections on startup from configured paths
133
- - **Multi-Format Support** - YAML (Sigma, Splunk), TOML (Elastic), Markdown (KQL)
229
+ - **Multi-Format Support** - YAML (Sigma, Splunk, Sublime, CrowdStrike CQL), TOML (Elastic), Markdown (KQL)
134
230
  - **Logsource Filtering** - Filter Sigma rules by category, product, or service
135
231
  - **Severity Filtering** - Filter by criticality level
136
232
 
@@ -170,7 +266,9 @@ Add to your MCP config (`~/.cursor/mcp.json` or `.cursor/mcp.json` in your proje
170
266
  "SPLUNK_PATHS": "/path/to/security_content/detections",
171
267
  "ELASTIC_PATHS": "/path/to/detection-rules/rules",
172
268
  "STORY_PATHS": "/path/to/security_content/stories",
173
- "KQL_PATHS": "/path/to/Hunting-Queries-Detection-Rules"
269
+ "KQL_PATHS": "/path/to/Hunting-Queries-Detection-Rules",
270
+ "SUBLIME_PATHS": "/path/to/sublime-rules/detection-rules",
271
+ "CQL_HUB_PATHS": "/path/to/cql-hub/queries"
174
272
  }
175
273
  }
176
274
  }
@@ -192,7 +290,9 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
192
290
  "SPLUNK_PATHS": "/Users/you/security_content/detections",
193
291
  "ELASTIC_PATHS": "/Users/you/detection-rules/rules",
194
292
  "STORY_PATHS": "/Users/you/security_content/stories",
195
- "KQL_PATHS": "/Users/you/Hunting-Queries-Detection-Rules"
293
+ "KQL_PATHS": "/Users/you/Hunting-Queries-Detection-Rules",
294
+ "SUBLIME_PATHS": "/Users/you/sublime-rules/detection-rules",
295
+ "CQL_HUB_PATHS": "/Users/you/cql-hub/queries"
196
296
  }
197
297
  }
198
298
  }
@@ -214,7 +314,9 @@ Add to `~/.vscode/mcp.json`:
214
314
  "SPLUNK_PATHS": "/Users/you/security_content/detections",
215
315
  "ELASTIC_PATHS": "/Users/you/detection-rules/rules",
216
316
  "KQL_PATHS": "/Users/you/kql-bertjanp,/Users/you/kql-jkerai1",
217
- "STORY_PATHS": "/Users/you/security_content/stories"
317
+ "STORY_PATHS": "/Users/you/security_content/stories",
318
+ "SUBLIME_PATHS": "/Users/you/sublime-rules/detection-rules",
319
+ "CQL_HUB_PATHS": "/Users/you/cql-hub/queries"
218
320
  }
219
321
  }
220
322
  }
@@ -236,7 +338,9 @@ Add to `~/.vscode/mcp.json`:
236
338
  "SPLUNK_PATHS": "/Users/you/security_content/detections",
237
339
  "ELASTIC_PATHS": "/Users/you/detection-rules/rules",
238
340
  "KQL_PATHS": "/Users/you/kql-bertjanp,/Users/you/kql-jkerai1",
239
- "STORY_PATHS": "/Users/you/security_content/stories"
341
+ "STORY_PATHS": "/Users/you/security_content/stories",
342
+ "SUBLIME_PATHS": "/Users/you/sublime-rules/detection-rules",
343
+ "CQL_HUB_PATHS": "/Users/you/cql-hub/queries"
240
344
  }
241
345
  }
242
346
  }
@@ -250,6 +354,8 @@ Add to `~/.vscode/mcp.json`:
250
354
  | `SPLUNK_PATHS` | Comma-separated paths to Splunk ESCU detection directories | At least one source required |
251
355
  | `ELASTIC_PATHS` | Comma-separated paths to Elastic detection rule directories | At least one source required |
252
356
  | `KQL_PATHS` | Comma-separated paths to KQL hunting query directories | At least one source required |
357
+ | `SUBLIME_PATHS` | Comma-separated paths to Sublime Security rule directories | At least one source required |
358
+ | `CQL_HUB_PATHS` | Comma-separated paths to CQL Hub (CrowdStrike) query directories | At least one source required |
253
359
  | `STORY_PATHS` | Comma-separated paths to Splunk analytic story directories | No (enhances context) |
254
360
 
255
361
  ## Getting Detection Content
@@ -278,11 +384,20 @@ cd detection-rules && git sparse-checkout set rules && cd ..
278
384
  git clone --depth 1 https://github.com/Bert-JanP/Hunting-Queries-Detection-Rules.git kql-bertjanp
279
385
  git clone --depth 1 https://github.com/jkerai1/KQL-Queries.git kql-jkerai1
280
386
 
387
+ # Download Sublime Security email detection rules (~900+ rules)
388
+ git clone --depth 1 --filter=blob:none --sparse https://github.com/sublime-security/sublime-rules.git
389
+ cd sublime-rules && git sparse-checkout set detection-rules && cd ..
390
+
391
+ # Download CQL Hub CrowdStrike queries (~139+ queries)
392
+ git clone --depth 1 https://github.com/ByteRay-Labs/Query-Hub.git cql-hub
393
+
281
394
  echo "Done! Configure your MCP with these paths:"
282
395
  echo " SIGMA_PATHS: $(pwd)/sigma/rules,$(pwd)/sigma/rules-threat-hunting"
283
396
  echo " SPLUNK_PATHS: $(pwd)/security_content/detections"
284
397
  echo " ELASTIC_PATHS: $(pwd)/detection-rules/rules"
285
398
  echo " KQL_PATHS: $(pwd)/kql-bertjanp,$(pwd)/kql-jkerai1"
399
+ echo " SUBLIME_PATHS: $(pwd)/sublime-rules/detection-rules"
400
+ echo " CQL_HUB_PATHS: $(pwd)/cql-hub/queries"
286
401
  echo " STORY_PATHS: $(pwd)/security_content/stories"
287
402
  ```
288
403
 
@@ -307,6 +422,14 @@ git clone https://github.com/elastic/detection-rules.git
307
422
  git clone https://github.com/Bert-JanP/Hunting-Queries-Detection-Rules.git
308
423
  git clone https://github.com/jkerai1/KQL-Queries.git
309
424
  # Use entire repos, combine paths with comma
425
+
426
+ # Sublime Security Rules
427
+ git clone https://github.com/sublime-security/sublime-rules.git
428
+ # Use detection-rules/ directory
429
+
430
+ # CQL Hub (CrowdStrike Query Language)
431
+ git clone https://github.com/ByteRay-Labs/Query-Hub.git
432
+ # Use queries/ directory
310
433
  ```
311
434
 
312
435
  ## 🆕 MCP Resources - Readable Context
@@ -336,7 +459,7 @@ The server provides **autocomplete suggestions** as you type argument values:
336
459
  | `process_name` | Process names in your detections (powershell.exe, etc.) |
337
460
  | `tactic` | All 14 MITRE tactics |
338
461
  | `severity` | informational, low, medium, high, critical |
339
- | `source_type` | sigma, splunk_escu, elastic, kql |
462
+ | `source_type` | sigma, splunk_escu, elastic, kql, sublime, crowdstrike_cql |
340
463
  | `threat_profile` | ransomware, apt, initial-access, persistence, etc. |
341
464
 
342
465
  This prevents typos and helps discover what values are available in your detection corpus.
@@ -395,7 +518,7 @@ Tool: prioritize_gaps(threat_profile="ransomware")
395
518
  | `search(query, limit)` | Full-text search across all detection fields (names, descriptions, queries, CVEs, process names, etc.) |
396
519
  | `get_by_id(id)` | Get a single detection by its ID |
397
520
  | `list_all(limit, offset)` | Paginated list of all detections |
398
- | `list_by_source(source_type)` | Filter by `sigma`, `splunk_escu`, `elastic`, or `kql` |
521
+ | `list_by_source(source_type)` | Filter by `sigma`, `splunk_escu`, `elastic`, `kql`, `sublime`, or `crowdstrike_cql` |
399
522
  | `get_raw_yaml(id)` | Get the original YAML/TOML/Markdown content |
400
523
  | `get_stats()` | Get index statistics |
401
524
  | `rebuild_index()` | Force re-index from configured paths |
@@ -746,7 +869,7 @@ Tool: list_by_cve(cve_id="CVE-2024-27198")
746
869
  ```
747
870
  LLM: "What detections do we have for credential dumping?"
748
871
  Tool: search(query="credential dumping", limit=10)
749
- → Returns results from Sigma, Splunk, Elastic, AND KQL
872
+ → Returns results from Sigma, Splunk, Elastic, KQL, Sublime, AND CrowdStrike CQL
750
873
  ```
751
874
 
752
875
  #### Find Web Server Attack Detections
@@ -771,6 +894,22 @@ LLM: "What KQL queries do we have for Defender For Endpoint?"
771
894
  Tool: list_by_kql_category(category="Defender For Endpoint")
772
895
  ```
773
896
 
897
+ #### Find BEC/Phishing Email Detections
898
+
899
+ ```
900
+ LLM: "What email detections do we have for BEC fraud?"
901
+ Tool: list_by_source(source_type="sublime")
902
+ → Returns Sublime Security email detection rules for BEC, phishing, malware, etc.
903
+ ```
904
+
905
+ #### Find CrowdStrike CQL Hunting Queries
906
+
907
+ ```
908
+ LLM: "What CrowdStrike queries do we have for lateral movement?"
909
+ Tool: list_by_source(source_type="crowdstrike_cql")
910
+ → Returns CQL Hub queries for CrowdStrike NextGen SIEM and Falcon LogScale
911
+ ```
912
+
774
913
  #### Search for BloodHound Detections
775
914
 
776
915
  ```
@@ -781,7 +920,7 @@ Tool: search(query="bloodhound", limit=10)
781
920
 
782
921
  ## Unified Schema
783
922
 
784
- All detection sources (Sigma, Splunk, Elastic, KQL) are normalized to a common schema:
923
+ All detection sources (Sigma, Splunk, Elastic, KQL, Sublime, CrowdStrike CQL) are normalized to a common schema:
785
924
 
786
925
  ### Core Fields
787
926
 
@@ -790,8 +929,8 @@ All detection sources (Sigma, Splunk, Elastic, KQL) are normalized to a common s
790
929
  | `id` | Unique identifier |
791
930
  | `name` | Detection name/title |
792
931
  | `description` | What the detection looks for |
793
- | `query` | Detection logic (Sigma YAML, Splunk SPL, Elastic EQL, or KQL) |
794
- | `source_type` | `sigma`, `splunk_escu`, `elastic`, or `kql` |
932
+ | `query` | Detection logic (Sigma YAML, Splunk SPL, Elastic EQL, KQL, Sublime MQL, or CrowdStrike CQL) |
933
+ | `source_type` | `sigma`, `splunk_escu`, `elastic`, `kql`, `sublime`, or `crowdstrike_cql` |
795
934
  | `severity` | Detection severity level |
796
935
  | `status` | Rule status (stable, test, experimental, production, etc.) |
797
936
  | `author` | Rule author |
@@ -823,6 +962,14 @@ All detection sources (Sigma, Splunk, Elastic, KQL) are normalized to a common s
823
962
  | `kql_keywords` | Security keywords extracted for search |
824
963
  | `platforms` | Platforms (windows, azure-ad, office-365, etc.) |
825
964
 
965
+ ### Sublime-Specific Fields
966
+
967
+ | Field | Description |
968
+ |-------|-------------|
969
+ | `sublime_attack_types` | Attack types (BEC/Fraud, Credential Phishing, Malware/Ransomware, etc.) |
970
+ | `sublime_detection_methods` | Detection methods (Content analysis, URL analysis, Computer Vision, etc.) |
971
+ | `sublime_tactics` | Tactics and techniques (Evasion, Impersonation: Brand, Social engineering, etc.) |
972
+
826
973
  ## Database
827
974
 
828
975
  The index is stored at `~/.cache/security-detections-mcp/detections.sqlite`.
@@ -860,6 +1007,22 @@ From [Elastic Detection Rules](https://github.com/elastic/detection-rules):
860
1007
  - Optional: `rule.description`, `rule.query`, `rule.severity`, `rule.tags`, `rule.threat` (MITRE mappings)
861
1008
  - Supports EQL, KQL, Lucene, and ESQL query languages
862
1009
 
1010
+ ### Sublime Security Rules (YAML)
1011
+
1012
+ From [Sublime Security](https://github.com/sublime-security/sublime-rules):
1013
+ - Required: `name`, `type` (rule/exclusion), `source` (MQL query)
1014
+ - Optional: `description`, `severity`, `id`, `references`, `tags`, `authors`, `attack_types`, `tactics_and_techniques`, `detection_methods`, `false_positives`
1015
+ - Uses MQL (Message Query Language) for email-specific detection logic
1016
+ - Covers BEC/fraud, credential phishing, malware delivery, spam, and more
1017
+
1018
+ ### CrowdStrike CQL Queries (YAML)
1019
+
1020
+ From [CQL Hub](https://github.com/ByteRay-Labs/Query-Hub):
1021
+ - Required: `name`, `cql` (CrowdStrike Query Language query)
1022
+ - Optional: `description`, `mitre_ids`, `author`, `log_sources`, `tags`, `cs_required_modules`, `explanation`
1023
+ - Community-driven detection and hunting queries for CrowdStrike NextGen SIEM and Falcon LogScale
1024
+ - Covers endpoint, network, cloud, and identity detection use cases
1025
+
863
1026
  ### KQL Hunting Queries (Markdown & Raw .kql)
864
1027
 
865
1028
  Supports multiple KQL repositories:
@@ -944,6 +1107,8 @@ SIGMA_PATHS="./detections/sigma/rules" \
944
1107
  SPLUNK_PATHS="./detections/splunk/detections" \
945
1108
  ELASTIC_PATHS="./detections/elastic/rules" \
946
1109
  KQL_PATHS="./detections/kql" \
1110
+ SUBLIME_PATHS="./detections/sublime-rules/detection-rules" \
1111
+ CQL_HUB_PATHS="./detections/cql-hub/queries" \
947
1112
  STORY_PATHS="./detections/splunk/stories" \
948
1113
  npm start
949
1114
  ```
@@ -958,11 +1123,13 @@ When fully indexed with all sources:
958
1123
  | Splunk ESCU | ~2,000+ |
959
1124
  | Elastic Rules | ~1,500+ |
960
1125
  | KQL Queries | ~420+ |
1126
+ | Sublime Rules | ~900+ |
1127
+ | CrowdStrike CQL | ~139+ |
961
1128
  | Analytic Stories | ~330 |
962
- | **Total Detections** | **~7,200+** |
1129
+ | **Total Detections** | **~8,200+** |
963
1130
  | **Indexed Patterns** | **10,235+** |
964
1131
  | **Techniques with Patterns** | **528+** |
965
- | **Detection Formats** | **4** (Sigma, Splunk, Elastic, KQL) |
1132
+ | **Detection Formats** | **6** (Sigma, Splunk, Elastic, KQL, Sublime, CrowdStrike CQL) |
966
1133
  | **Total Tools** | **71+** |
967
1134
  | **MCP Prompts** | **11** |
968
1135
  | **MCP Resources** | **9 static + 5 templates** |
@@ -1067,7 +1234,7 @@ For detailed information on v2.1 features:
1067
1234
 
1068
1235
  | MCP | Purpose |
1069
1236
  |-----|---------|
1070
- | **security-detections-mcp** | Query 7,200+ detection rules + 11 expert workflow prompts |
1237
+ | **security-detections-mcp** | Query 8,100+ detection rules + 11 expert workflow prompts |
1071
1238
  | **mitre-attack-mcp** | ATT&CK framework data, threat groups, Navigator layers |
1072
1239
 
1073
1240
  ### With MCP Prompts (Easiest)
@@ -1133,7 +1300,9 @@ LLM workflow:
1133
1300
  "SIGMA_PATHS": "/path/to/sigma/rules",
1134
1301
  "SPLUNK_PATHS": "/path/to/security_content/detections",
1135
1302
  "ELASTIC_PATHS": "/path/to/detection-rules/rules",
1136
- "KQL_PATHS": "/path/to/kql-hunting-queries"
1303
+ "KQL_PATHS": "/path/to/kql-hunting-queries",
1304
+ "SUBLIME_PATHS": "/path/to/sublime-rules/detection-rules",
1305
+ "CQL_HUB_PATHS": "/path/to/cql-hub/queries"
1137
1306
  }
1138
1307
  },
1139
1308
  "mitre-attack": {
@@ -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;
@@ -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
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
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;