CQL Pattern Catalog
Curated, battle-tested CQL patterns for detection engineering in CrowdStrike NG-SIEM. This is a pattern catalog, not an API reference — it shows how to combine CQL functions into effective detections. For function-level documentation, see crowdstrike-resources/docs/CQL/.
Pattern Categories
| Category |
File |
When to Use |
| Correlation |
patterns/correlation.md |
Multi-event detection: defineTable chains, correlate() sequences, readFile merge |
| Enrichment |
patterns/enrichment.md |
Adding context: join, selfJoinFilter, match() with CSV lookups, ipLocation, aid_master |
| Aggregation |
patterns/aggregation.md |
Summarizing events: groupBy with thresholds, bucket() time windows, timeChart, session() |
| String & Decode |
patterns/string-and-decode.md |
Parsing data: regex named captures, base64Decode, parseXml, bitfield:extractFlags, kvParse |
| Scoring |
patterns/scoring.md |
Risk assessment: weighted case{} scoring, severity tiering, slidingTimeWindow+rulesHit |
| Baselining |
patterns/baselining.md |
Anomaly detection: neighbor() sequential analysis, time-window baselines, geography:distance |
| Output |
patterns/output.md |
Formatting results: table vs select, format() for deep links, unit:convert, formatTime |
Routing Logic
Load pattern files based on your current task:
| Task |
Load These |
| Writing a new detection |
scoring + correlation |
| Hunting / investigation |
enrichment + aggregation |
| Tuning an existing detection |
baselining + scoring |
| Debugging query output |
output + string-and-decode |
| All tasks |
Read this entry point first for global gotchas below |
Global CQL Gotchas
Critical pitfalls that apply across all CQL work. Read these before writing any query.
# prefix REQUIRED for tagged fields. #event_simpleName=DnsRequest not event_simpleName=DnsRequest — unprefixed silently returns 0 results with no error.
Query optimization order. Put cheap filters first, expensive operations last:
time filter -> tag filter -> field filter -> negative filter -> regex -> functions -> aggregation -> rename -> join -> view
table() vs select(). table() creates a new result set and is an aggregation (limits to 200 rows by default). select() picks fields from existing results without limiting rows. Use select() unless you specifically need aggregation behavior.
String comparison is case-sensitive by default. Use /pattern/i for case-insensitive matching.
join() default mode is inner. Use mode=left to preserve all events from the base query when the join table has no match.
IP enrichment chain order. Run in this sequence — each adds fields the next can use:
ipLocation() -> asn() -> rdns(). Run these after groupBy() so they execute once per unique IP, not once per raw event.
Saved search description limit: 2000 characters. Keep description brief; put full documentation in queryString comments.
NG-SIEM query timeout: ~120 seconds. Break complex queries into stages using defineTable to avoid timeouts.
Pre-calculate arithmetic. Do division/multiplication before case/test blocks, not inside them.
Handle null baselines. Always handle missing data with case { field!=* | default; * } or default().
Profile with explain:asTable(). Append to any query to get per-stage performance metrics (timeMs, event counts, prefilter effectiveness). Use it to validate optimization order and find bottlenecks. Ad hoc only — do not include in scheduled searches, triggers, or dashboards. Not supported with correlate(). See output patterns for details.
Raw Reference
For complete CQL function API documentation, see crowdstrike-resources/docs/CQL/docs/CrowdStrike-Query-Language/combined.md or individual function files in that directory.
1---2name: cql-patterns3description: CQL pattern catalog — curated detection engineering patterns for CrowdStrike NG-SIEM. Use when writing, reviewing, or debugging CQL queries.4---56# CQL Pattern Catalog78Curated, battle-tested CQL patterns for detection engineering in CrowdStrike NG-SIEM. This is a pattern catalog, not an API reference — it shows *how* to combine CQL functions into effective detections. For function-level documentation, see `crowdstrike-resources/docs/CQL/`.910## Pattern Categories1112| Category | File | When to Use |13|----------|------|-------------|14| Correlation | `patterns/correlation.md` | Multi-event detection: defineTable chains, correlate() sequences, readFile merge |15| Enrichment | `patterns/enrichment.md` | Adding context: join, selfJoinFilter, match() with CSV lookups, ipLocation, aid_master |16| Aggregation | `patterns/aggregation.md` | Summarizing events: groupBy with thresholds, bucket() time windows, timeChart, session() |17| String & Decode | `patterns/string-and-decode.md` | Parsing data: regex named captures, base64Decode, parseXml, bitfield:extractFlags, kvParse |18| Scoring | `patterns/scoring.md` | Risk assessment: weighted case{} scoring, severity tiering, slidingTimeWindow+rulesHit |19| Baselining | `patterns/baselining.md` | Anomaly detection: neighbor() sequential analysis, time-window baselines, geography:distance |20| Output | `patterns/output.md` | Formatting results: table vs select, format() for deep links, unit:convert, formatTime |2122## Routing Logic2324Load pattern files based on your current task:2526| Task | Load These |27|------|------------|28| Writing a new detection | scoring + correlation |29| Hunting / investigation | enrichment + aggregation |30| Tuning an existing detection | baselining + scoring |31| Debugging query output | output + string-and-decode |32| All tasks | Read this entry point first for global gotchas below |3334## Global CQL Gotchas3536Critical pitfalls that apply across all CQL work. Read these before writing any query.37381. **`#` prefix REQUIRED for tagged fields.** `#event_simpleName=DnsRequest` not `event_simpleName=DnsRequest` — unprefixed silently returns 0 results with no error.39402. **Query optimization order.** Put cheap filters first, expensive operations last:41 > time filter -> tag filter -> field filter -> negative filter -> regex -> functions -> aggregation -> rename -> join -> view42433. **`table()` vs `select()`.** `table()` creates a new result set and is an aggregation (limits to 200 rows by default). `select()` picks fields from existing results without limiting rows. Use `select()` unless you specifically need aggregation behavior.44454. **String comparison is case-sensitive by default.** Use `/pattern/i` for case-insensitive matching.46475. **`join()` default mode is `inner`.** Use `mode=left` to preserve all events from the base query when the join table has no match.48496. **IP enrichment chain order.** Run in this sequence — each adds fields the next can use:50 `ipLocation()` -> `asn()` -> `rdns()`. Run these *after* `groupBy()` so they execute once per unique IP, not once per raw event.51527. **Saved search description limit: 2000 characters.** Keep `description` brief; put full documentation in `queryString` comments.53548. **NG-SIEM query timeout: ~120 seconds.** Break complex queries into stages using `defineTable` to avoid timeouts.55569. **Pre-calculate arithmetic.** Do division/multiplication *before* case/test blocks, not inside them.575810. **Handle null baselines.** Always handle missing data with `case { field!=* | default; * }` or `default()`.596011. **Profile with `explain:asTable()`.** Append to any query to get per-stage performance metrics (timeMs, event counts, prefilter effectiveness). Use it to validate optimization order and find bottlenecks. **Ad hoc only** — do not include in scheduled searches, triggers, or dashboards. Not supported with `correlate()`. See output patterns for details.6162## Raw Reference6364For complete CQL function API documentation, see `crowdstrike-resources/docs/CQL/docs/CrowdStrike-Query-Language/combined.md` or individual function files in that directory.