Help us improve
Share bugs, ideas, or general feedback.
From crowdstrike-logscale-security-queries
Develop, optimize, and troubleshoot CrowdStrike LogScale (Humio) security detection queries using CQL syntax. Use when writing LogScale queries, building security detections, creating threat hunting rules, fixing CQL syntax errors, working with CrowdStrike EDR/Falcon security monitoring, or building behavioral rules with the correlate() function. Handles case statements, risk categorization, multi-event correlation, investigation playbooks, and actionable security outputs.
npx claudepluginhub willwebster5/agent-skills --plugin crowdstrike-logscale-security-queriesHow this skill is triggered — by the user, by Claude, or both
Slash command
/crowdstrike-logscale-security-queries:logscale-security-queriesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Expert assistance for developing security detection queries and hunting rules in CrowdStrike LogScale (formerly Humio) using CQL syntax.
CQL pattern catalog — curated detection engineering patterns for CrowdStrike NG-SIEM. Use when writing, reviewing, or debugging CQL queries.
Executes threat hunting queries using SentinelOne PowerQuery on Singularity Data Lake, explains pipeline syntax, manages time ranges with get_timestamp_range and iso_to_unix_timestamp, analyzes results.
Unified SOC analyst workflow for CrowdStrike NGSIEM — triage alerts, investigate security events, hunt threats, and tune detections. Use when triaging alerts, investigating detections, running daily SOC review, or tuning for false positives.
Share bugs, ideas, or general feedback.
Expert assistance for developing security detection queries and hunting rules in CrowdStrike LogScale (formerly Humio) using CQL syntax.
Use this skill when you need to:
correlate() for multi-event detection// 1. Filter relevant events
#event_simpleName=<EventType>
| <field_filters>
// 2. Categorize risk
| case {
<critical_condition> | _RiskLevel := "Critical" ;
<high_condition> | _RiskLevel := "High" ;
* | _RiskLevel := "Low" ;
}
// 3. Enrich with context
| match(file="entraidusers.csv", field=UserPrincipalName, include=[DisplayName])
// 4. Generate output
| table([_RiskLevel, DisplayName, <key_fields>])
// ALWAYS use test() for numeric comparisons
| case {
test(FailedLogins > 5) | _Severity := "Critical" ; // ✅ CORRECT
FailedLogins > 5 | _Severity := "Critical" ; // ❌ WRONG
}
// AND/OR ARE supported in case branches (contrary to earlier docs)
// ✅ WORKS in case statements:
| case {
DetectionTier = "RAPID" AND UniqueIPs > 1 | RiskScore := 90;
UniqueIPs > 1 AND UniqueDevices > 1 | AttackPattern := "Distributed";
* | RiskScore := 50;
}
// Use composite keys when AND/OR is cleaner for string matching
| _Key := format("%s-%s", field=[Type, Location])
| case {
_Key="Admin-External" | _Risk := "High" ;
* | _Risk := "Low" ;
}
// ALWAYS include default branch
| case {
Status="Active" | _Label := "Active" ;
* | _Label := "Unknown" ; // ✅ Required
}
For multi-event pattern detection (attack chains, behavioral sequences):
correlate(
FailedLogin: {
event.outcome="failure" event.action=/UserLogon/
},
SuccessLogin: {
event.outcome="success" event.action=/UserLogon/
| user.email <=> FailedLogin.user.email
},
sequence=true,
within=30m,
globalConstraints=[user.email]
)
Key correlate() concepts:
FailedLogin: { ... } - each event pattern has a name<=>: Correlates fields between queriessequence=true: Events must occur in orderwithin=30m: Time window constraintglobalConstraints: Fields all events must shareSee correlate-function.md for complete syntax reference.
1. Actionable Over Raw
2. Syntax Precision
test() for all comparisons (>, <, >=, <=, !=):= for assignments in case statements; semicolon3. Maintainability
4. Risk-Based Categorization
See case-statements.md for:
See query-patterns.md for:
See troubleshooting.md for:
See investigation-playbooks.md for:
See examples.md for 11 production examples:
| case {
condition1 | field1 := value1 | field2 := value2 ;
test(comparison) | field := value ;
Field=/regex/ | field := value ;
* | field := default ; // Always required
}
test(Field > 5)test(Field != "value")test(Field1 > Field2)Field="value" (no test() needed)Field=/pattern/ (no test() needed)AND/OR works in case branches for combining conditions:
| case {
DetectionTier = "RAPID" AND UniqueIPs > 1 | RiskScore := 90;
entra.privilege_category="global_admin" AND CountryRisk!="low" | CompositeRiskScore := CompositeRiskScore + 25;
* | RiskScore := 50;
}
Composite keys are still useful for complex string-based matching:
| _Key := format("%s-%s-%s", field=[Protocol, Port, DestIP])
| case {
_Key="tcp-22-0.0.0.0/0" | _Risk := "Critical" ;
_Key=/tcp-(80|443)-.*/ | _Risk := "Low" ;
}
| Function | Purpose | Example |
|---|---|---|
test() | Numeric comparison in case/filter | test(count > 5) |
if() | Conditional value assignment | if(field, then=a, else=b) |
format() | String formatting | format("%s-%s", field=[a, b]) |
coalesce() | First non-null value | coalesce([f1, f2], as=out) |
defineTable() | Historical baseline | defineTable(query={...}, start=7d) |
match() | CSV/table lookup | match(file="x.csv", field=f) |
selfJoinFilter() | Multi-event correlation | selfJoinFilter(field=[aid], where=[...]) |
session() | Time-bounded grouping | session([...], maxpause=15m) |
array:contains() | Array membership | array:contains(array="f[]", value="v") |
objectArray:eval() | Iterate object arrays | objectArray:eval("arr[]", ...) |
shannonEntropy() | String randomness | shannonEntropy(DomainName) |
ipLocation() | GeoIP enrichment | ipLocation(source.ip) |
asn() | ASN organization lookup | asn(source.ip) |
cidr() | Subnet membership | cidr(ip, subnet=["10.0.0.0/8"]) |
now() | Current time (epoch ms) | _current := now() |
formatDuration() | Duration display | formatDuration(ms, from=ms, precision=2) |
See reference.md for complete syntax documentation.
// The <=> operator links fields between named queries
correlate(
Query1: { filter1 },
Query2: {
filter2
| fieldA <=> Query1.fieldB // Links fieldA to Query1.fieldB
},
within=1h
)
// Output fields are prefixed: Query1.fieldB, Query2.fieldA
correlate() function reference for behavioral rules and multi-event detectionWhen creating or modifying detection templates, always validate queries before committing:
# Validate query from a detection template
python scripts/resource_deploy.py validate-query --template <path/to/detection.yaml>
# Validate inline query
python scripts/resource_deploy.py validate-query --query '#Vendor="sase" | count()'
# Validate query from file
python scripts/resource_deploy.py validate-query --file /tmp/query.txt
VALID (exit code 0) - Query syntax is correctINVALID: <message> (exit code 1) - Query has syntax errorssearch.filter querypython scripts/resource_deploy.py validate-query --template <path>test(), missing default branch *)if() function (use case statements instead)test() wrapperVALIDpython scripts/resource_deploy.py plan --resources=detection| Error Pattern | Likely Cause | Fix |
|---|---|---|
NotAFunctionArgumentOperator | Using = in function args like count(x, where=field="value") | Use case statement to create flag field, then sum() |
UnrecognizedNamedArgumentNoSuggestions | Wrong if() syntax | Use case statement instead of if() |
ArraysNotSupportedHere | Positional args in if() | Use named params: if(condition, then=x, else=y) |
| Generic syntax error | Case statement issues | Check for test(), default branch, no AND/OR |
Unknown error with groupBy | Named assignment := in function list | Use as= for count/sum/min/max, use original field name for collect() |
Unknown error with collect | Using as= or := with collect() | collect() doesn't support naming - use original field name after groupBy |
When you get INVALID: Syntax error: Unknown error, isolate the problem:
# 1. Stash changes, validate original
git stash && python scripts/resource_deploy.py validate-query --template <path>
git stash pop
# 2. Test individual syntax patterns
python scripts/resource_deploy.py validate-query --query '#Vendor="aws" | groupBy([x], function=[count()])'
# 3. Binary search - comment out half the query and validate
See troubleshooting.md for the full debugging methodology.
:= assignment in groupBy function listcollect() doesn't support as= parameter - use original field namefield1="a" AND field2 > 5)*)as= for count/sum/min/max in groupBy<=> operator for linking (no other comparisons)globalConstraints to simplify linkssequence=true only when event order mattersThis skill works with: