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 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 **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
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
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](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
- [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](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 KQL detections from a single interface
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 `kql` |
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 KQL
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 KQL) |
885
- | `source_type` | `sigma`, `splunk_escu`, `elastic`, or `kql` |
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** | **~7,200+** |
1127
+ | **Total Detections** | **~8,200+** |
1054
1128
  | **Indexed Patterns** | **10,235+** |
1055
1129
  | **Techniques with Patterns** | **528+** |
1056
- | **Detection Formats** | **4** (Sigma, Splunk, Elastic, KQL) |
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 7,200+ detection rules + 11 expert workflow prompts |
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": {
@@ -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;
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
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
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
  }