| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 9 | 58 | 0% |
| Commands | 0 | 0 | 0 | — |
| Section tags | 1 | 1 | 3 | 20% |
What each file covers
Sections
0 shared · 9 only in A · 58 only in B- − .clinerules Directory Overview
- − Existing Components
- − Personas (`./personas/`)
- − Runbooks (`./run_books/`)
- − Suggested Additional Context Files
- − 1. Environment & Infrastructure Context
- − 2. Tool Configuration & Usage
- − 3. Organizational Policies & Procedures
- − 4. Threat Intelligence & Context
- + YARA-L 2.0 language syntax
- + Rule structure
- + Meta section syntax
- + Events section syntax
- + Variable declarations
- + Event variable filters
- + Event variable joins
- + Match section syntax
- + Zero value handling in the match section
- + Hop window
- + Sliding window
- + Outcome section syntax
- + Outcome variable data types
- + Conditional logic
- + Mathematical operations
- + Placeholder variables in outcomes
- + Outcome variables in outcome assignment expressions
- + Aggregations
- + Condition section syntax
- + Count character
- + Value character
- + Event and placeholder conditionals
- + Bounded and Unbounded conditions
- + Outcome conditionals
- + Options section syntax
- + allow\_zero\_values
- + suppression\_window
- + Composite detection rules
- + Use detections as input to rules
- + Combine events and detections
- + Create sequential composite detections
- + Boolean expressions
- + Comparisons
- + Functions
- + Reference list expressions
- + Logical expressions
- + Enumerated types
- + Nocase Modifier
- + Repeated fields
- + Repeated fields and boolean expressions
- + Repeated fields and placeholders
- + Array indexing
- + Repeated messages
- + Comments
- + Literals
- + String and regular expression literals
- + Operators
- + Variables
- + Keywords
- + Maps
- + arrays.concat
- + arrays.join\_string
- + arrays.length
- + arrays.max
- + arrays.min
- + arrays.size
- + arrays.index\_to\_float
- + arrays.index\_to\_int
Commands
neither file has anySection tags
1 shared · 1 only in A · 3 only in B- − setup
- + types
- + do-not
- + docs
- architecture
Line diff
repulsivityy/elevate_2025 · .clinerules/readme.md
@@ −1 @@
1# .clinerules Directory Overview
2
3This directory contains configuration files and documentation to provide context and guidance for LLM Agents operating within this security environment.
4
5## Existing Components
6
7### Personas (`./personas/`)
8
9* **Purpose:** These files define standard roles within the security operations team (e.g., SOC Analyst Tiers 1-3, Incident Responder, Threat Hunter, CTI Researcher, Security Engineer, Compliance Manager, SOC Manager).
10* **Content:** Each persona description outlines typical responsibilities, required skills, commonly used MCP tools, and relevant runbooks.
11* **Usage by LLM Agent:** Helps the agent understand user intent, tailor responses and actions to the user's likely role and perspective, and select appropriate tools and runbooks.
12
13### Runbooks (`./run_books/`)
14
15* **Purpose:** These files contain documented, step-by-step procedures or workflows for specific security operations tasks (e.g., triaging alerts, investigating IOCs, hunting for threats, responding to phishing).
16* **Content:** They often include objectives, scope, required inputs, specific MCP tools to use, workflow steps (sometimes visualized with diagrams like Mermaid), and expected outcomes.
17* **Usage by LLM Agent:** Serves as a primary plan for executing common security workflows, ensuring adherence to established procedures, guiding tool selection and sequencing, and promoting consistency.
18* **IRP vs. Runbook Distinction:** While all files here serve as procedural guides, we differentiate between:
19 * **Incident Response Plans (IRPs):** Located in the `./run_books/irps/` subdirectory, these outline the *end-to-end strategy* for handling major incident types (e.g., malware, phishing) following the full PICERL lifecycle. They orchestrate multiple steps and often call other runbooks. Use these as the starting point for major incident types.
20 * **Runbooks:** Located directly within `./run_books/` or in `./run_books/common_steps/`, these provide detailed, *tactical steps* for specific tasks (e.g., enriching an IOC, triaging an alert, isolating an endpoint) or reusable procedures. They are often components within a larger IRP.
21
22## Suggested Additional Context Files
23
24The following types of files could further enhance an LLM Agent's effectiveness:
25
26### 1. Environment & Infrastructure Context
27
28* **`network_map.md`**: Describes key network segments (e.g., DMZ, production servers, user subnets), their IP ranges, and primary functions. Helps in understanding the context of network events and potential lateral movement.
29* **`asset_inventory_guidelines.md`**: Outlines naming conventions for hosts/servers, common OS types, and potentially maps critical assets to their roles or owners. Helps contextualize alerts involving specific hosts.
30* **`critical_applications.md`**: Lists key business applications, their associated servers/IPs, and expected communication patterns. Useful for identifying anomalous behavior related to core services.
31* **`cloud_architecture.md`**: Provides an overview of the cloud environment structure (e.g., GCP project organization, key services like GKE, Cloud SQL), relevant for cloud-focused investigations (using SCC, etc.).
32
33### 2. Tool Configuration & Usage
34
35* **`tool_configurations.md`**: Details specific configurations crucial for tool usage, like:
36 * Important Chronicle Reference List names (e.g., `IP_Blocklist`, `Domain_Allowlist`) and their purpose.
37 * Key SOAR playbook names/IDs and what triggers them.
38 * Default timeframes or limits preferred for certain searches.
39* **`mcp_tool_best_practices.md`**: Offers tips or preferred syntax for using specific MCP tools effectively (e.g., optimizing `search_security_events` queries, interpreting specific GTI fields).
40* **`tool_rate_limits.md`**: Explicitly lists known rate limits or quotas for tools (like the Chronicle UDM query limit mentioned for a SOAR action) to help manage usage.
41
42### 3. Organizational Policies & Procedures
43
44* **`incident_severity_matrix.md`**: Defines how incident severity (Low, Medium, High, Critical) is determined based on impact and threat type. Aids in prioritization.
45* **`escalation_paths.md`**: Outlines who to notify or escalate to under specific circumstances (e.g., confirmed ransomware, PII exposure).
46* **`reporting_templates.md`**: Provides standard formats or key sections required for different types of reports (e.g., daily SOC summary, post-incident report).
47* **`approved_remediations.md`**: Lists standard, pre-approved containment or remediation actions for common, lower-severity findings.
48* **`key_contacts.md`**: Lists relevant teams or individuals for specific issues (e.g., Network Ops, Identity Team, Legal).
49
50### 4. Threat Intelligence & Context
51
52* **`internal_threat_profile.md`**: Details specific threat actors, campaigns, or TTPs that are of high concern to *this specific organization*.
53* **`allowlists.md`**: Lists organization-specific known-good IPs, domains, file hashes, or process names that should generally be ignored unless context suggests otherwise.
54* **`common_benign_alerts.md`**: Describes alerts often triggered by known benign activity (e.g., vulnerability scans, specific admin scripts) and how to typically handle them.
55
56Having these additional context files would allow the LLM Agent to perform more nuanced analysis, make better-informed decisions, adhere more closely to organizational standards, and require less clarification during complex tasks.
57
repulsivityy/elevate_2025 · .clinerules/YARAL_SYNTAX.md
@@ +1 @@
1
2# YARA-L 2.0 language syntax
3
4Supported in:
5
6Google secops
7[Siem](/chronicle/docs/secops/google-secops-siem-toc)
8
9This section describes the major elements of the YARA-L syntax. See also [Overview of the YARA-L 2.0 language](/chronicle/docs/detection/yara-l-2-0-overview).
10
11**Note:** YARA-L syntax doesn't allow negative integers. For example,
12`$e.principal.ip[-1]` is not valid. Replace `-1` with `0-1`.
13
14## Rule structure
15
16For YARA-L 2.0, you must specify variable declarations, definitions, and usages in the following order:
17
181. meta
192. events
203. match (optional)
214. outcome (optional)
225. condition
236. options (optional)
24
25**Note:** If you exclude `match`, the rule can match against a single event.
26
27The following example illustrates the generic structure of a rule:
28
29```
30rule <rule Name>
31{
32 meta:
33 // Stores arbitrary key-value pairs of rule details, such as who wrote
34 // it, what it detects on, version control, etc.
35
36 events:
37 // Conditions to filter events and the relationship between events.
38
39 match:
40 // Values to return when matches are found.
41
42 outcome:
43 // Additional information extracted from each detection.
44
45 condition:
46 // Condition to check events and the variables used to find matches.
47
48 options:
49 // Options to turn on or off while executing this rule.
50}
51
52```
53
54## Meta section syntax
55
56Meta section is composed of multiple lines, where each line defines a key-value pair. A key part must be an unquoted string, and a value part must be a quoted string:
57
58`<key> = "<value>"`
59
60The following is an example of a valid `meta` section line:
61
62```
63meta:
64 author = "Google"
65 severity = "HIGH"
66
67```
68
69## Events section syntax
70
71In the `events` section, list the predicates to specify the following:
72
73* Variable declarations
74* Event variable filters
75* Event variable joins
76
77### Variable declarations
78
79For variable declarations, use the following syntax:
80
81* `<EVENT_FIELD> = <VAR>`
82* `<VAR> = <EVENT_FIELD>`
83
84Both are equivalent, as shown in the following examples:
85
86* `$e.source.hostname = $hostname`
87* `$userid = $e.principal.user.userid`
88
89This declaration indicates that this variable represents the specified field for the event variable. When the event field is a repeated field, the match variable can represent any value in the array. It is also possible to assign multiple event fields to a single match or placeholder variable. This is a transitive join condition.
90
91For example, the following:
92
93* `$e1.source.ip = $ip`
94* `$e2.target.ip = $ip`
95
96Are equivalent to:
97
98* `$e1.source.ip = $ip`
99* `$e1.source.ip = $e2.target.ip`
100
101When a variable is used, the variable must be declared through variable declaration. If a variable is used without any declaration, it is regarded as a compilation error.
102
103### Event variable filters
104
105A [boolean expression](#boolean_expressions) that acts on a single event variable is considered a filter.
106
107### Event variable joins
108
109All event variables used in the rule must be joined with every other event variable in either of the following ways:
110
111* Directly through an equality comparison between event fields of the two joined event variables, for example: `$e1.field = $e2.field`. The expression must not include arithmetic.
112* Indirectly through a transitive join involving only an event field (see [variable declaration](#variable_declarations) for a definition of "transitive join"). The expression must not include arithmetic.
113
114For example, assuming $e1, $e2, and $e3 are used in the rule, the following `events` sections are valid.
115
116```
117events:
118 $e1.principal.hostname = $e2.src.hostname // $e1 joins with $e2
119 $e2.principal.ip = $e3.src.ip // $e2 joins with $e3
120
121```
122
123```
124events:
125 // $e1 joins with $e2 via function to event comparison
126 re.capture($e1.src.hostname, ".*") = $e2.target.hostname
127
128```
129
130```
131events:
132 // $e1 joins with $e2 via an `or` expression
133 $e1.principal.hostname = $e2.src.hostname
134 or $e1.principal.hostname = $e2.target.hostname
135 or $e1.principal.hostname = $e2.principal.hostname
136
137```
138
139```
140events:
141 // all of $e1, $e2 and $e3 are transitively joined via the placeholder variable $ip
142 $e1.src.ip = $ip
143 $e2.target.ip = $ip
144 $e3.about.ip = $ip
145
146```
147
148```
149events:
150 // $e1 and $e2 are transitively joined via function to event comparison
151 re.capture($e2.principal.application, ".*") = $app
152 $e1.principal.hostname = $app
153
154```
155
156**Note:** If your sole join condition is an `or` chain, a function to event
157comparison, or a combination of both, then the rule may perform poorly.
158
159However, here are examples of invalid `events` sections.
160
161```
162events:
163 // Event to arithmetic comparison is an invalid join condition for $e1 and $e2.
164 $e1.principal.port = $e2.src.port + 1
165
166```
167
168```
169events:
170 $e1.src.ip = $ip
171 $e2.target.ip = $ip
172 $e3.about.ip = "192.1.2.0" //$e3 is not joined with $e1 or $e2.
173
174```
175
176```
177events:
178 $e1.src.port = $port
179
180 // Arithmetic to placeholder comparison is an invalid transitive join condition.
181 $e2.principal.port + 800 = $port
182
183```
184
185## Match section syntax
186
187In the `match` section, list the match variables for group events before checking for match conditions. Those fields are returned with each match.
188
189* Specify what each match variable represents in the `events` section.
190* Specify the time duration to use to correlate events after the `over` keyword. Events outside the time duration are ignored.
191* Use the following syntax to specify the time duration: `<number><m/h/d>`
192
193 Where `m/h/d` means minutes, hours, and days respectively.
194* Minimum time you can specify is 1 minute.
195* Maximum time you can specify is 48 hours.
196
197The following is an example of a valid `match`:
198
199```
200$var1, $var2 over 5m
201
202```
203
204This statement returns `$var1` and `$var2` (defined in the `events` section) when the rule finds a match. The time specified is 5 minutes. Events that are more than 5 minutes apart are not correlated and therefore ignored by the rule.
205
206Here is another example of a valid `match` section:
207
208```
209$user over 1h
210
211```
212
213This statement returns `$user` when the rule finds a match. The time window specified is 1 hour. Events that are more than an hour apart are not correlated. The rule does not consider them to be a detection.
214
215Here is another example of a valid `match` section:
216
217```
218$source_ip, $target_ip, $hostname over 2m
219
220```
221
222This statement returns `$source_ip`, `$target_ip`, and `$hostname` when the rule finds a match. The time window specified is 2 minutes. Events that are more than 2 minutes apart are not correlated. The rule does not consider them to be a detection.
223
224The following examples illustrate **invalid** `match` sections:
225
226* `var1, var2 over 5m // invalid variable name`
227* `$user 1h // missing keyword`
228
229### Zero value handling in the match section
230
231Rules Engine implicitly filters out the zero values for all placeholders that
232are used in the match section (`""` for
233string, `0` for numbers, `false` for booleans, the value in position 0
234for [enumerated types](/chronicle/docs/reference/udm-field-list#event_enumerated_types)).
235The following example illustrates rules that filter out the zero values.
236
237```
238rule ZeroValuePlaceholderExample {
239 meta:
240 events:
241 // Because $host is used in the match section, the rule behaves
242 // as if the following predicate was added to the events section:
243 // $host != ""
244 $host = $e.principal.hostname
245
246 // Because $otherPlaceholder was not used in the match section,
247 // there is no implicit filtering of zero values for $otherPlaceholder.
248 $otherPlaceholder = $e.principal.ip
249
250 match:
251 $host over 5m
252
253 condition:
254 $e
255}
256
257```
258
259However, if a placeholder is assigned to a function, rules don't
260implicitly filter out the zero values of placeholders that are used in
261the match section.
262The following example illustrates rules that filter out the zero values:
263
264```
265rule ZeroValueFunctionPlaceholder {
266 meta:
267 events:
268 // Even though $ph is used in the match section, there is no
269 // implicit filtering of zero values for $ph, because $ph is assigned to a function.
270 $ph = re.capture($e.principal.hostname, "some-regex")
271
272 match:
273 $ph over 5m
274
275 condition:
276 $e
277}
278
279```
280
281To disable the implicit filtering of zero values,
282you can use the `allow_zero_values` option in the [options section](#options_section_syntax).
283
284### Hop window
285
286By default, YARA-L 2.0 rules with a match section are evaluated using hop windows.
287The time range of the rule's execution is divided into a set of overlapping hop windows,
288each with the duration specified in the `match` section. Events are then correlated
289within each hop window.
290
291For example, for a rule that is run over the time range [1:00, 2:00], with a
292`match` section over `30m`, a possible set of overlapping hop windows
293that could be generated is [1:00, 1:30], [1:03, 1:33] and [1:06, 1:36].
294These windows are used to correlate multiple events.
295
296### Sliding window
297
298Using hop windows is not an effective way to search for events that happen in a specific order (for example, `e1` happens up to 2
299minutes after `e2`). An occurrence of event `e1` and an occurrence of event `e2`
300are correlated only if they fall into the same hop window generated.
301
302A more effective way to search for such event sequences is to use sliding windows.
303Sliding windows with the duration specified in the `match` section are generated when
304beginning or ending with a specified pivot event variable. Events are then
305correlated within each sliding window. This makes it possible to search for
306events that happen in a specific order (for example, `e1` happens within 2
307minutes of `e2`). An occurrence of event `e1` and an occurrence of event `e2`
308are correlated if event `e1` occurs within the sliding window duration after
309event `e2`.
310
311Specify sliding windows in the `match` section of a rule as follows:
312
313`<match-var-1>, <match-var-2>, ... over <duration> before|after <pivot-event-var>`
314
315The pivot event variable is the event variable that sliding windows are based
316on. If you use the `before` keyword, sliding windows are generated, ending with
317each occurrence of the pivot event. If the `after` keyword is used, sliding
318windows are generated beginning with each occurrence of the pivot event.
319
320The following are examples of valid sliding window usages:
321
322* `$var1, $var2 over 5m after $e1`
323* `$user over 1h before $e2`
324
325See [a sliding window rule example](/chronicle/docs/detection/yara-l-2-0-overview#sliding_window_rule_example).
326
327**Note:** Using sliding windows instead of hop windows has been known to result in
328slower performance. We recommend using sliding windows only for
329specific cases, such as when event order is absolutely necessary or when
330searching for the non-existence of events.
331
332We recommend not using sliding windows for single-event rules, because
333sliding windows are designed to detect multiple events. If one of
334your rules falls in this category, We recommend one of
335the following workarounds:
336
337* Convert the rule to use multiple event variables, and update the condition
338 section if the rule requires more than one occurrence of the event.
339 + Optionally, consider adding timestamp filters instead of using a sliding window.
340 For example, `$permission_change.metadata.event_timestamp.seconds < $file_creation.metadata.event_timestamp.seconds`
341* Remove the sliding window.
342
343## Outcome section syntax
344
345In the `outcome` section, you can define up to 20 outcome variables, with
346arbitrary names. These outcomes will be stored in the detections generated by
347the rule. Each detection may have different values for the outcomes.
348
349The outcome name, `$risk_score`, is special. You can optionally define an
350outcome with this name, and if you do, it must be an integer or float type. If populated,
351the `risk_score` will be shown in the
352[Enterprise Insights view](https://cloud.google.com/chronicle/docs/investigation/view-alerts-insights.md) for
353alerts that come from rule detections.
354
355If you don't include a `$risk_score` variable in the outcome section of a rule,
356one of the following default values is set:
357
358* If the rule is configured to generate an alert, then `$risk_score` is set to 40.
359* If the rule is not configured to generate an alert, then `$risk_score` is set to 15.
360
361The value of `$risk_score` is stored in the `security_result.risk_score` UDM field.
362
363### Outcome variable data types
364
365Each outcome variable can have a different data type, which is determined by the expression
366used to compute it. We support the following outcome data types:
367
368* integer
369* floats
370* string
371* lists of integers
372* lists of floats
373* lists of strings
374
375### Conditional logic
376
377You can use conditional logic to compute the value of an outcome. Conditionals
378are specified using the following syntax pattern:
379
380```
381if(BOOL_CLAUSE, THEN_CLAUSE)
382if(BOOL_CLAUSE, THEN_CLAUSE, ELSE_CLAUSE)
383
384```
385
386You can read a conditional expression as "if BOOL\_CLAUSE is true, then return
387THEN\_CLAUSE, else return ELSE\_CLAUSE".
388
389BOOL\_CLAUSE must evaluate to a boolean value. A BOOL\_CLAUSE expression takes a
390similar form as expressions in the `events` section. For example, it can
391contain:
392
393* UDM field names with comparison operator, for example:
394
395 `if($context.graph.entity.user.title = "Vendor", 100, 0)`
396* placeholder variable that was defined in the `events` section, for example:
397
398 `if($severity = "HIGH", 100, 0)`
399* another outcome variable defined in the `outcome` section, for example:
400
401 `if($risk_score > 20, "HIGH", "LOW")`
402* functions that return a boolean, for example:
403
404 `if(re.regex($e.network.email.from, `.*altostrat.com`), 100, 0)`
405* look up in a [reference list](#reference_lists_syntax), for example:
406
407 `if($u.principal.hostname in %my_reference_list_name, 100, 0)`
408* aggregation comparison, for example:
409
410 `if(count($login.metadata.event_timestamp.seconds) > 5, 100, 0)`
411
412The THEN\_CLAUSE and ELSE\_CLAUSE must be the same data type. We support integers, floats, and strings.
413
414You can omit the ELSE\_CLAUSE if the data type is integer or a float. If omitted, the
415ELSE\_CLAUSE evaluates to 0. For example:
416
417```
418`if($e.field = "a", 5)` is equivalent to `if($e.field = "a", 5, 0)`
419
420```
421
422You must provide the ELSE\_CLAUSE if the data type is string or if the THEN\_CLAUSE
423is a placeholder variable or outcome variable.
424
425### Mathematical operations
426
427You can use mathematical operations to compute integer or float data type in the `outcome`and `events` sections of a rule. Google Security Operations supports addition, subtraction, multiplication, division, and modulus as top level operators in a computation.
428
429The following snippet is an example computation in the `outcome` section:
430
431```
432outcome:
433 $risk_score = max(100 + if($severity = "HIGH", 10, 5) - if($severity = "LOW", 20, 0))
434
435```
436
437Mathematical operations are allowed on the following types of operands as long as
438each operand and the entire arithmetic expression is properly aggregated (See [Aggregations](#aggregations)):
439
440* Numeric event fields
441* Numeric placeholder variables defined in the `events` section
442* Numeric outcome variables defined in the `outcome` section
443* Functions returning ints or floats
444* Aggregations returning ints or floats
445
446Modulus is not allowed on floats.
447
448### Placeholder variables in outcomes
449
450When computing outcome variables, you can use placeholder variables which were
451defined in the events section of your rule. In this example, assume that
452`$email_sent_bytes` was defined in the events section of the rule:
453
454Single-event example:
455
456```
457// No match section, so this is a single-event rule.
458
459outcome:
460 // Use placeholder directly as an outcome value.
461 $my_outcome = $email_sent_bytes
462
463 // Use placeholder in a conditional.
464 $other_outcome = if($file_size > 1024, "SEVERE", "MODERATE")
465
466condition:
467 $e
468
469```
470
471Multi-event example:
472
473```
474match:
475 // This is a multi event rule with a match section.
476 $hostname over 5m
477
478outcome:
479 // Use placeholder directly in an aggregation function.
480 $max_email_size = max($email_sent_bytes)
481
482 // Use placeholder in a mathematical computation.
483 $total_bytes_exfiltrated = sum(
484 1024
485 + $email_sent_bytes
486 + $file_event.principal.file.size
487 )
488
489condition:
490 $email_event and $file_event
491
492```
493
494### Outcome variables in outcome assignment expressions
495
496Outcome variables can be used to derive other outcome variables, similar to
497placeholder variables defined in the `events` section. You can refer to an outcome
498variable in the assignment of another outcome variable with a `$` token followed
499by the variable name. Outcome variables must be defined before they can be referenced
500in the rule text. When used in an assignment expression, outcome variables must
501not be aggregated (See [Aggregations](#aggregations)).
502
503In the following example, the outcome variable `$risk_score` derives its
504value from the outcome variable `$event_count`:
505
506Multi-event example:
507
508```
509match:
510 // This is a multi event rule with a match section.
511 $hostname over 5m
512
513outcome:
514 // Aggregates all timestamp on login events in the 5 minute match window.
515 $event_count = count($login.metadata.event_timestamp.seconds)
516
517 // $event_count cannot be aggregated again.
518 $risk_score = if($event_count > 5, "SEVERE", "MODERATE")
519
520 // This is the equivalent of the 2 outcomes above combined.
521 $risk_score2 = if(count($login.metadata.event_timestamp.seconds) > 5, "SEVERE", "MODERATE")
522
523condition:
524 $e
525
526```
527
528Outcome variables can be used in any type of expression on the right-hand-side of an outcome assignment,
529except in the following expressions:
530
531* Aggregations
532* `Arrays.length()` function calls
533* With `any` or `all` modifiers
534
535### Aggregations
536
537Repeated event fields are non-scalar values. That is, a single variable points to
538multiple values. For example, the event field variable `$e.target.ip` is a repeated field
539and can have zero, one, or many ip values. It is a non-scalar value. Whereas the event field variable
540`$e.principal.hostname` is not a repeated field and only has 1 value (i.e. a scalar value).
541
542Similarly, both non-repeated event fields and repeated event fields used in the outcome section
543of a rule with a match window are non-scalar values. For example, the following rule groups events
544using a match section and refers to a non-repeated event field in the outcome section:
545
546```
547rule OutcomeAndMatchWindow{
548 ...
549 match:
550 $userid over 5m
551 outcome:
552 $hostnames = array($e.principal.hostname)
553 ...
554}
555
556```
557
558Any 5-minute window the rule executes over might contain zero, one, or many events. The outcome section
559operates on all events in a match window. Any event field variable referred to within the
560outcome section can point to zero, one, or many values of the field on each event in the match window.
561For example, if a 5-minute window contains 5 `$e` events, `$e.principal.hostname`
562in the outcome section points to five different hostnames. The event field variable
563`$e.principal.hostname` is treated as a non-scalar value in the `outcome` section of this rule.
564
565Because outcome variables must always yield a single scalar value, any non-scalar value which
566an outcome assignment depends on must be aggregated to yield a single scalar value.
567In an outcome section, the following are non-scalar values and must be aggregated:
568
569* Event fields (repeated or non-repeated) when the rule uses a match section
570* Event placeholders (repeated or non-repeated) when the rule uses a match section
571* Repeated event fields when the rule does not use a match section
572* Repeated event placeholders when the rule does not use a match section
573
574Scalar event fields, scalar event placeholders, and constants can be wrapped in
575aggregation functions in rules that don't include a match section. However, in
576most cases, these aggregations return the wrapped value, making them unnecessary.
577An exception is the `array()` aggregation, which you can use to explicitly convert
578a scalar value into an array.
579
580Outcome variables are treated like aggregations: they must not be re-aggregated
581when referred to in another outcome assignment.
582
583You can use the following aggregation functions:
584
585* `max()`: outputs the maximum over all possible values. Only works with integer and float.
586* `min()`: outputs the minimum over all possible values. Only works with integer and float.
587* `sum()`: outputs the sum over all possible values. Only works with integer and float.
588* `count_distinct()`: collects all possible values, then outputs the distinct count of
589 possible values.
590* `count()`: behaves like `count_distinct()`, but returns a non-distinct count of
591 possible values.
592* `array_distinct()`: collects all possible distinct values, then outputs a list of these values. It
593 will truncate the list of distinct values to 25 random elements. The deduplication
594 to get a distinct list is applied first, then the truncation is applied.
595* `array()`: behaves like `array_distinct()`, but returns a non-distinct list of
596 values. It also truncates the list of values to 25 random elements.
597* `period_start_for_max()`: start of the time period where the maximum of
598 the listed value occurred.
599* `period_start_for_min()`: start of the time period where the minimum of
600 the listed value occurred.
601
602The aggregate function is important when a rule includes a `condition` section
603that specifies multiple events must exist, because the aggregate function will
604operate on all the events that generated the detection.
605
606For example, if your `outcome` and `condition` sections contain:
607
608```
609outcome:
610 $asset_id_count = count($event.principal.asset_id)
611 $asset_id_distinct_count = count_distinct($event.principal.asset_id)
612
613 $asset_id_list = array($event.principal.asset_id)
614 $asset_id_distinct_list = array_distinct($event.principal.asset_id)
615
616condition:
617 #event > 1
618
619```
620
621Since the condition section requires there to be more than one `event` for each
622detection, the aggregate functions will operate on multiple events. Suppose the
623following events generated one detection:
624
625```
626event:
627 // UDM event 1
628 asset_id="asset-a"
629
630event:
631 // UDM event 2
632 asset_id="asset-b"
633
634event:
635 // UDM event 3
636 asset_id="asset-b"
637
638```
639
640Then the values of your outcomes will be:
641
642* $asset\_id\_count = `3`
643* $asset\_id\_distinct\_count = `2`
644* $asset\_id\_list = `["asset-a", "asset-b", "asset-b"]`
645* $asset\_id\_distinct\_list = `["asset-a", "asset-b"]`
646
647#### Things to know when using the outcome section:
648
649Other notes and restrictions:
650
651* The `outcome` section cannot reference a new placeholder variable which
652 wasn't already defined in the `events` section or in the `outcome` section.
653* The `outcome` section cannot use event variables that have not
654 been defined in the `events` section.
655* The `outcome` section can use an event field that was not
656 used in the `events` section, given that the event variable that the event
657 field belongs to was already defined in the `events` section.
658* The `outcome` section can only correlate event variables that have already
659 been correlated in the `events` section. Correlations happen when two
660 event fields from different event variables are equated.
661
662You can find an example using the outcome section in
663[Overview of the YARA-L 2.0](/chronicle/docs/detection/yara-l-2-0-overview#rule_with_outcome_section_example).
664See [Create context-aware analytics](/chronicle/docs/detection/context-aware-analytics#outcome_section) for details on detection
665deduping with the outcome section.
666
667## Condition section syntax
668
669* specify a match condition over events and placeholders defined in the `events` section. See the following section, *Event and placeholder conditionals*, for more details.
670* (optional) use the `and` keyword to specify a match condition using outcome variables defined in the `outcome` section. See the following section, *Outcome conditionals*, for more details.
671
672### Count character
673
674The `#` character is a special character in the `condition` section. If it is
675used before any event or placeholder variable name, it represents the number of
676distinct events or values that satisfy all of the `events` section conditions.
677
678For example, `#c > 1` means the variable `c` must occur more than 1 time.
679
680### Value character
681
682The `$` character is a special character in the `condition` section. If it is
683used before any outcome variable name, it represents the value of that outcome.
684
685If it is used before any event or placeholder variable name (for example,
686`$event`), it represents `#event > 0`.
687
688### Event and placeholder conditionals
689
690List condition predicates for events and placeholder variables here, joined
691with the keyword `and` or `or`. The keyword `and` can be used between any
692conditions, but the keyword `or` can only be used when the rule only has a
693single event variable.
694
695A valid example of using `or` between two placeholders on the same event:
696
697```
698rule ValidConditionOr {
699 meta:
700 events:
701 $e.metadata.event_type = "NETWORK_CONNECTION"
702
703 // Note that all placeholders use the same event variable.
704 $ph = $e.principal.user.userid // Define a placeholder variable to put in match section.
705 $ph2 = $e.principal.ip // Define a second placeholder variable to put in condition section.
706 $ph3 = $e.principal.hostname // Define a third placeholder variable to put in condition section.
707
708 match:
709 $ph over 5m
710
711 condition:
712 $ph2 or $ph3
713}
714
715```
716
717An invalid example of using `or` between two conditions on different events:
718
719```
720rule InvalidConditionOr {
721 meta:
722 events:
723 $e.metadata.event_type = "NETWORK_CONNECTION"
724 $e2.graph.metadata.entity_type = "FILE"
725 $e2.graph.entity.hostname = $e.principal.hostname
726
727 $ph = $e.principal.user.userid // Define a placeholder variable to put in match section.
728
729 match:
730 $ph over 5m
731
732 condition:
733 $e or $e2 // This line will cause an error because there is an or between events.
734}
735
736```
737
738**Note:** Don't use the keyword `not` in event and placeholder conditionals.
739
740### Bounded and Unbounded conditions
741
742The following conditions are bounded conditions. They force the associated
743event variable to exist, meaning that at least one occurrence of the event must
744appear in any detection.
745
746* `$var // equivalent to #var > 0`
747* `#var > n // where n >= 0`
748* `#var >= m // where m > 0`
749
750The following conditions are unbounded conditions. They allow the associated
751event variable to not exist, meaning that it is possible that no occurrence of
752the event appears in a detection and any reference to fields on the event
753variable will yield a zero value. Unbounded conditions can be used to detect
754the absence of an event over a period of time. For example, a threat event
755without a mitigation event within a 10 minute window. Rules using unbounded
756conditions are called non-existence rules.
757
758* `!$var // equivalent to #var = 0`
759* `#var >= 0`
760* `#var < n // where n > 0`
761* `#var <= m // where m >= 0`
762
763**Note:** For non-existence rules, the detection engine adds a 1 hour delay to the
764expected latency (based on the rule's run frequency) to allow for late-arriving
765data.
766
767#### Requirements for non-existence
768
769For a rule with non-existence to compile, it must satisfy the following requirements:
770
7711. At least one UDM event must have a bounded condition (that is, at least one UDM event must exist).
7722. If a placeholder has an unbounded condition, it must be associated with
773 at least one bounded UDM event.
7743. If an entity has an unbounded condition, it must be associated with at
775 least one bounded UDM event.
776
777Consider the following rule with the condition section omitted:
778
779```
780rule NonexistenceExample {
781 meta:
782 events:
783 $u1.metadata.event_type = "NETWORK_CONNECTION" // $u1 is a UDM event.
784 $u2.metadata.event_type = "NETWORK_CONNECTION" // $u2 is a UDM event.
785 $e1.graph.metadata.entity_type = "FILE" // $e1 is an Entity.
786 $e2.graph.metadata.entity_type = "FILE" // $e2 is an Entity.
787
788 $user = $u1.principal.user.userid // Match variable is required for Multi-Event Rule.
789
790 // Placeholder Associations:
791 // u1 u2
792 // | \ /
793 // port ip
794 // | \
795 // e1 e2
796 $u1.target.port = $port
797 $e1.graph.entity.port = $port
798 $u1.principal.ip = $ip
799 $u2.target.ip = $ip
800 $e2.graph.entity.ip = $ip
801
802 // UDM-Entity Associations:
803 // u1 - u2
804 // | \ |
805 // e1 e2
806 $u1.metadata.event_type = $u2.metadata.event_type
807 $e1.graph.entity.hostname = $u1.principal.hostname
808 $e2.graph.entity.hostname = $u1.target.hostname
809 $e2.graph.entity.hostname = $u2.principal.hostname
810
811 match:
812 $user over 5m
813
814 condition:
815 <condition_section>
816}
817
818```
819
820The following are *valid* examples for the `<condition_section>`:
821
822* `$u1 and !$u2 and $e1 and $e2`
823 + All UDM events and entities are present in the condition section.
824 + At least one UDM event is bounded.
825* `$u1 and !$u2 and $e1 and !$e2`
826 + `$e2`is unbounded, which is allowed because it is associated with `$u1`, which is bounded. If `$e2` was not associated with `$u1`, this would be invalid.
827* `#port > 50 and #ip = 0`
828 + No UDM events and entities are present in the condition section; however, the placeholders that are present cover all the UDM events and entities.
829 + `$ip` is assigned to both `$u1` and `$u2` and `#ip = 0` is an unbounded condition. However, bounded conditions are *stronger* than unbounded conditions. Since `$port` is assigned to `$u1` and `#port > 50` is a bounded condition, `$u1` is still bounded.
830
831The following are *invalid* examples for the `<condition_section>`:
832
833* `$u1 and $e1`
834 + Every UDM event and entity appearing in the Events Section must appear in
835 the Condition Section (or have a placeholder assigned to it that appears in the Condition Section).
836* `$u1, $u2, $e1, $u2, #port > 50`
837 + Commas are not allowed as condition separators.
838* `!$u1 and !$u2 and $e1 and $e2`
839 + Violates the first requirement that at least one UDM event is bounded.
840* `($u1 or #port < 50) and $u2 and $e1 and $e2`
841 + `or` keyword is not supported with unbounded conditions.
842* `($u1 or $u2) and $e1 and $e2`
843 + `or` keyword is not supported between different event variables.
844* `not $u1 and $u2 and $e1 and $e2`
845 + `not` keyword is not allowed for event and placeholder conditions.
846* `#port < 50 and #ip = 0`
847 + The placeholders that are present cover all the UDM events and entities; however, all of the conditions are unbounded. This means none of the UDM events are bounded, causing the rule to fail to compile.
848
849**Note:** Don't use a `match` variable in the `condition` section. It is a semantic
850error since events are grouped by the `match` variable value.**Note:** Don't specify only **unbounded conditions** on all `event` variables that a `match` variable is assigned to. It is a semantic error. For a `match` variable value to be returned, at least one event must exist that contains the value.**Note:** In case of using a sliding window, the pivot event variable must be involved in at least one bounded condition.
851
852### Outcome conditionals
853
854List condition predicates for outcome variables here, joined with the keyword `and` or `or`, or preceded by the keyword `not`.
855
856Specify outcome conditionals differently depending on the type of the outcome variable:
857
858* **integer**: compare against an integer literal with operators `=, >, >=, <, <=, !=`, for example:
859
860 `$risk_score > 10`
861* **float**: compare against a float literal with operators `=, >, >=, <, <=, !=`, for example:
862
863 `$risk_score <= 5.5`
864* **string**: compare against a string literal with either `=` or `!=`, for example:
865
866 `$severity = "HIGH"`
867* **list of integers or arrays**: specify condition using the `arrays.contains` function, for example:
868
869 `arrays.contains($event_ids, "id_1234")`
870
871**Note:** If you use the keyword `or` inside the <event/placeholder conditionals> subsection, you must surround that entire subsection with parentheses.
872For example, the following is valid: `($e1 or $e2) and $outcome > 0`.
873
874#### Rule classification
875
876Specifying an outcome conditional *in a rule that has a match section* means that the rule will be classified as a **multi-event** rule for rule quota.
877See [single event rule](/chronicle/docs/detection/yara-l-2-0-overview#single_event_rule) and [multiple event rule](/chronicle/docs/detection/yara-l-2-0-overview#multiple_event_rule) for more information about single and multiple event classifications.
878
879## Options section syntax
880
881In the `options` section, you can specify the options for the rule. Here is
882an example of how to specify the options section:
883
884```
885rule RuleOptionsExample {
886 // Other rule sections
887
888 options:
889 allow_zero_values = true
890}
891
892```
893
894You can specify options using the syntax `key = value`, where `key` must be a
895predefined option name and `value` must be a valid value for the option, as
896specified for the following options:
897
898### allow\_zero\_values
899
900The valid values for this option are `true` and `false`, which determine
901if this option is enabled or not. The default value is `false`. This option is
902disabled if it is not specified in the rule.
903
904To enable this setting, add the following
905to the options section of your rule: `allow_zero_values = true`. Doing so
906will prevent the rule from implicitly filtering out the
907zero values of placeholders that are used in the match section, as
908described in [zero value handling in the match section](#zero_value_handling_in_the_match_section).
909
910### suppression\_window
911
912The `suppression_window` option lets you control how often a rule triggers a
913detection. It prevents the same rule from generating multiple detections within
914a specified time window, even if the rule's conditions are met multiple times.
915Suppression windowing uses a tumbling window approach, which suppresses
916duplicates over a fixed-size, non-overlapping window.
917
918You can optionally provide a `suppression_key` to further refine which instances
919of the rule are suppressed within the suppression window. If not specified, all
920instances of the rule are suppressed. This key is defined as an outcome variable.
921
922In the following example, `suppression_window` is set to `5m` and `suppression_key` is
923set to the `$hostname` variable. After the rule triggers a detection for
924`$hostname`, any further detections for `$hostname` are suppressed for the next
925five minutes. However, if the rule triggers on an event with a different hostname,
926a detection is created.
927
928The default value of `suppression_window` is `0`; that is, the suppression
929window is disabled by default. This option only works for [single event rules](/chronicle/docs/detection/yara-l-2-0-overview#single-event-rule)
930that don't have a `match` section.
931
932Example:
933
934```
935rule SuppressionWindowExample {
936 // Other rule sections
937
938 outcome:
939 $suppression_key = $hostname
940
941 options:
942 suppression_window = 5m
943}
944
945```
946
947## Composite detection rules
948
949**Note:** This feature is covered by [Pre-GA Offerings Terms](https://chronicle.security/legal/service-terms/) of the Google Security Operations Service
950Specific Terms. Pre-GA features might have limited support, and changes to pre-GA features might not be compatible with other pre-GA versions.
951For more information, see the [Google SecOps Technical Support Service guidelines](https://chronicle.security/legal/technical-support-services-guidelines/)
952and the [Google SecOps Service Specific Terms](https://chronicle.security/legal/service-terms/).
953
954Composite detection in Google SecOps involves connecting multiple
955YARA-L rules. This sections explains how to build a
956composite rule. For an overview of composite detections,
957see [Overview of composite detections](/chronicle/docs/detection/composite-detections).
958
959### Rule structure
960
961Composite detection rules are always multi-event rules and follow the same
962[structure and syntax](/chronicle/docs/detection/yara-l-2-0-syntax#rule_structure).
963The following requirements apply to composite detection rules:
964
965* Composite rules must use a `match` section to define detection trigger conditions.
966* Rules that use both detection fields and UDM events must explicitly join these
967 data sources.
968
969For information on rule limitations, see [Limitations](/chronicle/docs/detection/composite-detections#limitations).
970
971### Use detections as input to rules
972
973Composite rules can reference rule detections generated by any custom or curated rule.
974Google SecOps provides two methods for doing this.
975
976#### Reference detection content using outcome variables, match variables, or meta labels
977
978To access data from a detection without referencing the original UDM events,
979use `outcome` variables, `match` variables, or `meta` labels. We recommend this
980approach because it provides greater flexibility and better compatibility across
981different rule types.
982
983For example, multiple rules can store a string (such as a URL, filename, or
984registry key) in a common `outcome` variable if you're looking for that string
985across different contexts. To access this string from a composite rule, start
986with `detection` and locate the relevant information using elements from the
987[Collection resource](/chronicle/docs/reference/rest/v1alpha/Collection).
988
989**Example:**
990For example, suppose a detection rule produces the following information:
991
992* Outcome variable: `dest_domain = "cymbal.com"`
993* UDM field: `target.hostname = "cymbal.com"`
994
995In the composite rule, you can access this data using the following paths:
996
997* `detection.detection.outcomes["dest_domain"]` to access the `dest_domain`
998 outcome variable.
999* `detection.collection_elements.references.event.target.hostname` to access
1000 the `target.hostname` UDM field.
1001* `detection.time_window.start_time.seconds` to access the detection timestamp.
1002
1003The Collection API and the `SecurityResult` API provide access to both:
1004
1005* Detection metadata and outcome values (`detection.detection`)
1006* Underlying UDM events from referenced rules (`collection_elements`)
1007
1008#### Reference detection content using rule ID or rule name
1009
1010You can reference a rule by either its name or ID. We recommend this
1011approach when your detection logic depends on specific rules. Referencing
1012relevant rules by name or ID improves performance and prevents timeouts by
1013reducing the data analyzed. For example, you can directly query fields like
1014`target.url` or `principal.ip` from a known previous detection.
1015
1016* **Reference a rule by rule ID (recommended):** use the
1017 `detection.detection.rule_id` field to reference a rule by ID. You can find the
1018 rule ID in the rule's URL in Google SecOps. User-generated rules
1019 have IDs in the format `ru_UUID`, while curated detections have IDs in the
1020 format `ur_UUID`. For example:
1021
1022 `detection.detection.rule_id = "ru_e0d3f371-6832-4d20-b0ad-1f4e234acb2b"`
1023* **Reference a rule by a rule name:** use the `detection.detection.rule_name`
1024 field to reference a rule by name. You can specify the exact rule name or use a
1025 regular expression to match it. For example:
1026
1027 + `detection.detection.rule_name = "My Rule Name"`
1028 + `detection.detection.rule_name = "/PartOfName/"`
1029
1030**Note:** We recommend using rule IDs for referencing because IDs are unique and
1031don't change. Rule names can be modified, which could potentially break your
1032composite detection.
1033
1034### Combine events and detections
1035
1036Composite rules can combine different data sources, including UDM events, entity
1037graph data, and detection fields. The following guidelines apply:
1038
1039* **Use distinct variables per source**—Assign unique event variables to each data source (for example, `$e` for
1040 events, `$d` for detections), where the data source includes events, entities,
1041 and detections.
1042* **Join sources on shared context**—Connect data sources using common values, such as user IDs, IP addresses, or
1043 domain names in your rule's conditions.
1044* **Define a match window**—Always include a `match` clause with a time window no longer than 48 hours.
1045
1046For example:
1047
1048```
1049rule CheckCuratedDetection_with_EDR_and_EG {
1050 meta:
1051 author = "noone@cymbal.com"
1052 events:
1053 $d.detection.detection.rule_name = /SCC: Custom Modules: Configurable Bad Domain/
1054 $d.detection.collection_elements.references.event.network.dns.questions.name = $domain
1055 $d.detection.collection_elements.references.event.principal.asset.hostname = $hostname
1056
1057 $e.metadata.log_type = "LIMACHARLIE_EDR"
1058 $e.metadata.product_event_type = "NETWORK_CONNECTIONS"
1059 $domain = re.capture($e.principal.process.command_line, "\\s([a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+)$")
1060 $hostname = re.capture($e.principal.hostname, "([^.]*)")
1061
1062 $prevalence.graph.metadata.entity_type = "DOMAIN_NAME"
1063 $prevalence.graph.metadata.source_type = "DERIVED_CONTEXT"
1064 $prevalence.graph.entity.hostname = $domain
1065 $prevalence.graph.entity.domain.prevalence.day_count = 10
1066 $prevalence.graph.entity.domain.prevalence.rolling_max <= 5
1067 $prevalence.graph.entity.domain.prevalence.rolling_max > 0
1068
1069 match:
1070 $hostname over 1h
1071
1072 outcome:
1073 $risk_score = 80
1074 $CL_target = array($domain)
1075
1076 condition:
1077 $e and $d and $prevalence
1078}
1079
1080```
1081
1082### Create sequential composite detections
1083
1084Sequential composite detections identify patterns of related events where the
1085sequence of detections is important, such as a brute-force login attempt
1086detection, followed by a successful login. These patterns can combine multiple
1087base detections, raw UDM events, or both.
1088
1089To create a sequential composite detection, you must enforce that order within
1090your rule. To enforce the expected sequence, use one of the following methods:
1091
1092* **Sliding windows:** Define the sequence of detections using sliding windows
1093 in your `match` conditions.
1094* **Timestamp comparisons:** Compare the timestamps of detections within your
1095 rule logic to ensure that they happen in the selected order.
1096
1097For example:
1098
1099```
1100events:
1101 $d1.detection.detection.rule_name = "fileEvent_rule"
1102 $userid = $d1.detection.detection.outcomes["user"]
1103 $hostname = $d1.detection.detection.outcomes["hostname"]
1104
1105 $d2.detection.detection.rule_name = "processExecution_rule"
1106 $userid = $d2.detection.detection.outcomes["user"]
1107 $hostname = $d2.detection.detection.outcomes["hostname"]
1108
1109 $d3.detection.detection.rule_name = "networkEvent_rule"
1110 $userid = $d3.detection.detection.outcomes["user"]
1111 $hostname = $d3.detection.detection.outcomes["hostname"]
1112
1113$d3.detection.collection_elements.references.event.metadata.event_timestamp.seconds > $d2.detection.collection_elements.references.event.metadata.event_timestamp.seconds
1114
1115 match:
1116 $userid over 24h after $d1
1117
1118```
1119
1120## Boolean expressions
1121
1122Boolean expressions are expressions with a boolean type.
1123
1124### Comparisons
1125
1126For a binary expression to use as condition, use the following syntax:
1127
1128* `<EXPR> <OP> <EXPR>`
1129
1130Expression can be either event field, variable, literal, or function expression.
1131
1132For example:
1133
1134* `$e.source.hostname = "host1234"`
1135* `$e.source.port < 1024`
1136* `1024 < $e.source.port`
1137* `$e1.source.hostname != $e2.target.hostname`
1138* `$e1.metadata.collected_timestamp.seconds > $e2.metadata.collected_timestamp.seconds`
1139* `$port >= 25`
1140* `$host = $e2.target.hostname`
1141* `"google-test" = strings.concat($e.principal.hostname, "-test")`
1142* `"email@google.org" = re.replace($e.network.email.from, "com", "org")`
1143
1144If both sides are literals, it is regarded as a compilation error.
1145
1146### Functions
1147
1148Some function expressions return boolean value, which can be used as an individual predicate in the `events` section. Such functions are:
1149
1150* `re.regex()`
1151* `net.ip_in_range_cidr()`
1152
1153For example:
1154
1155* `re.regex($e.principal.hostname, `.*\.google\.com`)`
1156* `net.ip_in_range_cidr($e.principal.ip, "192.0.2.0/24")`
1157
1158### Reference list expressions
1159
1160You can use reference lists in the events section. See the section on
1161[Reference Lists](#reference_lists_syntax) for more details.
1162
1163### Logical expressions
1164
1165You can use the logical `and` and logical `or` operators in the `events` section as shown in the following examples:
1166
1167* `$e.metadata.event_type = "NETWORK_DNS" or $e.metadata.event_type = "NETWORK_DHCP"`
1168* `($e.metadata.event_type = "NETWORK_DNS" and $e.principal.ip = "192.0.2.12") or ($e.metadata.event_type = "NETWORK_DHCP" and $e.principal.mac = "AB:CD:01:10:EF:22")`
1169* `not $e.metadata.event_type = "NETWORK_DNS"`
1170
1171By default, the precedence order from highest to lowest is `not`, `and`, `or`.
1172
1173For example, "a or b and c" is evaluated as "a or (b and c)" when the operators `or` and `and` are defined explicitly in the expression.
1174
1175In the `events` section, predicates are joined using the `and` operator if an operator is not explicitly defined.
1176
1177The order of evaluation may be different if the `and` operator is implied in the expression.
1178
1179For example, consider the following comparison expressions where `or` is defined explicitly. The `and` operator is implied.
1180
1181```
1182$e1.field = "bat"
1183or $e1.field = "baz"
1184$e2.field = "bar"
1185
1186```
1187
1188This example is interpreted as follows:
1189
1190```
1191($e1.field = "bat" or $e1.field = "baz")
1192and ($e2.field = "bar")
1193
1194```
1195
1196Because `or` is defined explicitly, the predicates surrounding `or` are grouped and evaluated first.
1197The last predicate, `$e2.field = "bar"` is joined implicitly using `and`. The result is that order of evaluation changes.
1198
1199**Note:** There is a limit on the number of `and` and `or` values you can specify for a
1200single rule. This limit varies depending on the complexity of the rule and the
1201complexity of the data in your Google SecOps account. Contact your Google SecOps representative for information on alternatives to this type of
1202rule.
1203
1204## Enumerated types
1205
1206You can use the operators with [enumerated](/chronicle/docs/reference/udm-field-list#event_enumerated_types) types. It can be applied to rules to simplify and optimize (use operator instead of reference lists) the performance.
1207
1208In the following example, 'USER\_UNCATEGORIZED' and 'USER\_RESOURCE\_DELETION' correspond to 15000 and 15014, so the rule will look for all the listed events:
1209
1210```
1211$e.metadata.event_type >= "USER_CATEGORIZED" and $e.metadata.event_type <= "USER_RESOURCE_DELETION"
1212
1213```
1214
1215List of events:
1216
1217* USER\_RESOURCE\_DELETION
1218* USER\_RESOURCE\_UPDATE\_CONTENT
1219* USER\_RESOURCE\_UPDATE\_PERMISSIONS
1220* USER\_STATS
1221* USER\_UNCATEGORIZED
1222
1223## Nocase Modifier
1224
1225When you have a comparison expression between string values or a regular expression, you can append nocase at the end of the expression to ignore capitalization.
1226
1227* `$e.principal.hostname != "http-server" nocase`
1228* `$e1.principal.hostname = $e2.target.hostname nocase`
1229* `$e.principal.hostname = /dns-server-[0-9]+/ nocase`
1230* `re.regex($e.target.hostname, `client-[0-9]+`) nocase`
1231
1232This cannot be used when a type of field is an enumerated value. The following
1233examples are invalid and will generate compilation errors:
1234
1235* `$e.metadata.event_type = "NETWORK_DNS" nocase`
1236* `$e.network.ip_protocol = "TCP" nocase`
1237
1238## Repeated fields
1239
1240In the Unified Data Model (UDM), some fields are labeled as repeated, which indicates
1241that they are lists of values or other types of messages.
1242
1243### Repeated fields and boolean expressions
1244
1245There are 2 kinds of boolean expressions that act on repeated fields:
1246
12471. Modified
12482. Unmodified
1249
1250Consider the following event:
1251
1252```
1253event_original {
1254 principal {
1255 // ip is a repeated field
1256 ip: [ "192.0.2.1", "192.0.2.2", "192.0.2.3" ]
1257
1258 hostname: "host"
1259 }
1260}
1261
1262```
1263
1264#### Modified expressions
1265
1266The following sections describe the purpose and how to use the `any` and `all` modifiers in expressions.
1267
1268##### any
1269
1270If *any* element of the repeated field satisfies the condition, the event as a whole satisfies the condition.
1271
1272* `event_original` satisfies `any $e.principal.ip = "192.0.2.1"`.
1273* `event_original` fails `any $e.repeated_field.field_a = "9.9.9.9`.
1274
1275##### all
1276
1277If *all* elements of the repeated field satisfy the condition, the event as a whole satisfies the condition.
1278
1279* `event_original` satisfies `net.ip_in_range_cidr(all $e.principal.ip, "192.0.2.0/8")`.
1280* `event_original` fails `all $e.principal.ip = "192.0.2.2"`.
1281
1282**Note:** To use `any` or `all` with a function, the modifier must precede the repeated field and not the function. For example, `re.regex(any $e.about.hostname, `server-[0-9]+`)` is valid while `any re.regex($e.about.hostname, `server-[0-9]+`)` is not.
1283
1284When writing a condition with `any` or `all`, be aware that negating the condition
1285with `not` might not have the same meaning as using the negated operator.
1286
1287For example:
1288
1289* `not all $e.principal.ip = "192.168.12.16"` checks if not all IP addresses
1290 match `192.168.12.16`, meaning the rule is checking whether at least one IP address
1291 does not match `192.168.12.16`.
1292* `all $e.principal.ip != "192.168.12.16"` checks if all IP addresses don't match
1293 `192.168.12.16`, meaning the rule is checking that no IP addresses match to `192.168.12.16`.
1294
1295Constraints:
1296
1297* `any` and `all` operators are only compatible with repeated fields (not scalar fields).
1298* `any` and `all` cannot be used to join two repeated fields. For example, `any $e1.principal.ip = $e2.principal.ip` is not valid.
1299* `any` and `all` operators are not supported with the reference list expression.
1300
1301#### Unmodified expressions
1302
1303With unmodified expressions, each element in the repeated field is treated individually. If an event's repeated field contains *n* elements, then the rule is applied on *n* copies of the event, where each copy has one of the elements of the repeated field. These copies are transient and not stored.
1304
1305The rule is applied on the following copies:
1306
1307| event copy | principal.ip | principal.hostname |
1308| --- | --- | --- |
1309| event\_copy\_1 | "192.0.2.1" | "host" |
1310| event\_copy\_2 | "192.0.2.2" | "host" |
1311| event\_copy\_3 | "192.0.2.3" | "host" |
1312
1313If *any* event copy satisfies *all* unmodified conditions on the repeated field, the event as a whole satisfies all the conditions. That means that if you have multiple conditions on a repeated field, then the event copy must satisfy *all* of them. The following rule examples use the preceding example dataset to demonstrate this behavior.
1314
1315The following rule returns one match when run against the `event_original` example
1316dataset, because `event_copy_1` satisfies all of the events predicates:
1317
1318```
1319rule repeated_field_1 {
1320 meta:
1321 events:
1322 net.ip_in_range_cidr($e.principal.ip, "192.0.2.0/8") // Checks if IP address matches 192.x.x.x
1323 $e.principal.ip = "192.0.2.1"
1324 condition:
1325 $e
1326}
1327
1328```
1329
1330The following rule doesn't return a match when run against the `event_original`
1331example dataset, because there is no event copy in `$e.principal.ip` that
1332satisfies *all* the event predicates.
1333
1334```
1335rule repeated_field_2 {
1336 meta:
1337 events:
1338 $e.principal.ip = "192.0.2.1"
1339 $e.principal.ip = "192.0.2.2"
1340 condition:
1341 $e
1342}
1343
1344```
1345
1346Modified expressions on repeated fields are compatible with unmodified expressions on repeated fields because the element list is the same for each event copy. Consider the following rule:
1347
1348```
1349rule repeated_field_3 {
1350 meta:
1351 events:
1352 any $e.principal.ip = "192.0.2.1"
1353 $e.principal.ip = "192.0.2.3"
1354 condition:
1355 $e
1356}
1357
1358```
1359
1360The rule is applied on the following copies:
1361
1362| event copy | principal.ip | any $e.principal.ip |
1363| --- | --- | --- |
1364| event\_copy\_1 | "192.0.2.1" | ["192.0.2.1", "192.0.2.2", "192.0.2.3"] |
1365| event\_copy\_2 | "192.0.2.2" | ["192.0.2.1", "192.0.2.2", "192.0.2.3"] |
1366| event\_copy\_3 | "192.0.2.3" | ["192.0.2.1", "192.0.2.2", "192.0.2.3"] |
1367
1368In this case, all copies satisfy `any $e.principal.ip = "192.0.2.1"` but only `event_copy_3` satisfies $e.principal.ip = "192.0.2.3". As a result, the event as a whole would match.
1369
1370Another way to think about these expression types are:
1371
1372* Expressions on repeated fields which use `any` or `all` operate on the list in `event_original`.
1373* Expressions on repeated fields which don't use `any` or `all` operate on individual `event_copy_n` events.
1374
1375### Repeated fields and placeholders
1376
1377Repeated fields work with placeholder assignments. Similar to unmodified expressions on repeated fields, a copy of the event is made for each element. Using the same example of `event_copy`, the placeholder takes the value of the `event_copy_n`'s repeated field value, for each of the event copies where *n* is the event copy number. If the placeholder is used in the match section, this can result in multiple matches.
1378
1379The following example generates one match. The `$ip` placeholder is equal
1380to `192.0.2.1` for `event_copy_1`, which satisfies the predicates in the rule.
1381The match's event samples contain a single element, `event_original`.
1382
1383```
1384// Generates 1 match.
1385rule repeated_field_placeholder1 {
1386 meta:
1387 events:
1388 $ip = $e.principal.ip
1389 $ip = "192.0.2.1"
1390 $host = $e.principal.hostname
1391
1392 match:
1393 $host over 5m
1394
1395 condition:
1396 $e
1397}
1398
1399```
1400
1401The following example generates three matches. The `$ip` placeholder is equal
1402to different values, for each of the different `event_copy_n` copies.
1403The grouping is done on `$ip` since it is in the match section. Therefore, you get three matches
1404where each match has a different value for the `$ip` match variable. Each match has the same
1405event sample: a single element, `event_original`.
1406
1407```
1408// Generates 3 matches.
1409rule repeated_field_placeholder2 {
1410 meta:
1411 events:
1412 $ip = $e.principal.ip
1413 net.ip_in_range_cidr($ip, "192.0.2.0/8") // Checks if IP matches 192.x.x.x
1414
1415 match:
1416 $ip over 5m
1417
1418 condition:
1419 $e
1420}
1421
1422```
1423
1424**Note:** `any` and `all` cannot be used when assigning a repeated field to a placeholder variable or joining with a field of another event. For example, `any $e.principal.ip = $ip` is not valid.
1425
1426#### Outcomes using placeholders assigned to repeated fields
1427
1428Placeholders are assigned to each *element* of each repeated field - not the entire list. Thus, when they're used in the outcome section, the outcome is calculated using only the elements that satisfied earlier sections.
1429
1430Consider the following rule:
1431
1432```
1433rule outcome_repeated_field_placeholder {
1434 meta:
1435 events:
1436 $ip = $e.principal.ip
1437 $ip = "192.0.2.1" or $ip = "192.0.2.2"
1438 $host = $e.principal.hostname
1439
1440 match:
1441 $host over 5m
1442
1443 outcome:
1444 $o = array_distinct($ip)
1445
1446 condition:
1447 $e
1448}
1449
1450```
1451
1452There are 4 stages of execution for this rule. The first stage is event copying:
1453
1454| event copy | $ip | $host | $e |
1455| --- | --- | --- | --- |
1456| event\_copy\_1 | "192.0.2.1" | "host" | event\_id |
1457| event\_copy\_2 | "192.0.2.2" | "host" | event\_id |
1458| event\_copy\_3 | "192.0.2.3" | "host" | event\_id |
1459
1460The events section will then filter out rows that don't match the filters:
1461
1462| event copy | $ip | $host | $e |
1463| --- | --- | --- | --- |
1464| event\_copy\_1 | "192.0.2.1" | "host" | event\_id |
1465| event\_copy\_2 | "192.0.2.2" | "host" | event\_id |
1466
1467`event_copy_3` is filtered out because `"192.0.2.3"` does not satisfy `$ip = "192.0.2.1" or $ip = "192.0.2.2"`.
1468
1469The match section will then group by match variables and the outcome section will perform aggregation on each group:
1470
1471| $host | $o | $e |
1472| --- | --- | --- |
1473| "host" | ["192.0.2.1", "192.0.2.2"] | event\_id |
1474
1475`$o = array_distinct($ip)` is calculated using `$ip` from the previous stage and not the event copying stage.
1476
1477Finally, the condition section will filter each group. Since this rule just checks for the existence of $e, the row from earlier will produce a single detection.
1478
1479`$o` does not contain all the elements from `$e.principal.ip` because not all the elements satisfied all the conditions in the events section. However, all the elements of `e.principal.ip` will appear in the event sample because the event sample uses `event_original`.
1480
1481### Array indexing
1482
1483You can perform array indexing on repeated fields. To access the n-th repeated field element, use the standard list syntax (elements are 0-indexed). An out-of-bounds element returns the default value.
1484
1485* `$e.principal.ip[0] = "192.168.12.16"`
1486* `$e.principal.ip[999] = ""` If there are fewer than 1000 elements, this evaluates to `true`.
1487
1488Constraints:
1489
1490* An index must be a non-negative integer literal. For example, `$e.principal.ip[-1]` is not valid.
1491* Values that have an `int` type (for example, a placeholder set to `int`) don't count.
1492* Array indexing cannot be combined with `any` or `all`. For example, `any $e.intermediary.ip[0]` is not valid.
1493* Array indexing cannot be combined with map syntax. For example, `$e.additional.fields[0]["key"]` is not valid.
1494* If the field path contains multiple repeated fields, all repeated fields must use array indexing. For example, `$e.intermediary.ip[0]` is not valid because `intermediary` and `ip` are both repeated fields, but there is only an index for `ip`.
1495
1496### Repeated messages
1497
1498When a [`message`](https://protobuf.dev/overview/#syntax) field is repeated, an unintended effect is to reduce the likelihood of a match. This is illustrated in the following examples.
1499
1500Consider the following event:
1501
1502```
1503event_repeated_message {
1504 // about is a repeated message field.
1505 about {
1506 // ip is a repeated string field.
1507 ip: [ "192.0.2.1", "192.0.2.2", "192.0.2.3" ]
1508
1509 hostname: "alice"
1510 }
1511 about {
1512 hostname: "bob"
1513 }
1514}
1515
1516```
1517
1518As stated for unmodified expressions on repeated fields, a temporary copy of the event is made for each element of the repeated field. Consider the following rule:
1519
1520```
1521rule repeated_message_1 {
1522 meta:
1523 events:
1524 $e.about.ip = "192.0.2.1"
1525 $e.about.hostname = "bob"
1526 condition:
1527 $e
1528}
1529
1530```
1531
1532The rule is applied on the following copies:
1533
1534| event copy | about.ip | about.hostname |
1535| --- | --- | --- |
1536| event\_copy\_1 | "192.0.2.1" | "alice" |
1537| event\_copy\_2 | "192.0.2.2" | "alice" |
1538| event\_copy\_3 | "192.0.2.3" | "alice" |
1539| event\_copy\_4 | "" | "bob" |
1540
1541The event does not match on the rule because there exists no event copy that satisfies all of the expressions.
1542
1543#### Repeated messages and array indexing
1544
1545Another unexpected behavior can occur when using array indexing with unmodified expressions on repeated message fields. Consider the following example rule which uses array indexing:
1546
1547```
1548rule repeated_message_2 {
1549 meta:
1550 events:
1551 $e.about.ip = "192.0.2.1"
1552 $e.about[1].hostname = "bob"
1553 condition:
1554 $e
1555}
1556
1557```
1558
1559The rule is applied to the following copies:
1560
1561| event copy | about.ip | about[1].hostname |
1562| --- | --- | --- |
1563| event\_copy\_1 | "192.0.2.1" | "bob" |
1564| event\_copy\_2 | "192.0.2.2" | "bob" |
1565| event\_copy\_3 | "192.0.2.3" | "bob" |
1566| event\_copy\_4 | "" | "bob" |
1567
1568Since `event_copy_1` satisfies all of the expressions in `repeated_message_2`, the event matches on the rule.
1569
1570This can lead to unexpected behavior because rule `repeated_message_1` lacked array indexing and produced no matches while rule `repeated_message_2` used array indexing and produced a match.
1571
1572## Comments
1573
1574Designate comments with two slash characters (`// comment`) or multi-line comments set off using slash asterisk characters (`/* comment */`), as you would in C.
1575
1576## Literals
1577
1578Nonnegative integers and floats, string, boolean, and regular expression literals are supported.
1579
1580### String and regular expression literals
1581
1582You can use either of the following quotation characters to enclose strings in YARA-L 2.0. However, quoted text is interpreted differently depending on which one you use.
1583
15841. Double quotes (") — Use for normal strings. Must include escape characters.
1585 For example: "hello\tworld" —\t is interpreted as a tab
15862. Back quotes (`) — Use to interpret all characters literally.
1587 For example: `hello\tworld` —\t is not interpreted as a tab
1588
1589For regular expressions, you have two options.
1590
1591If you want to use regular expressions directly without the `re.regex()` function, use `/regex/` for the regular expression literals.
1592
1593You can also use string literals as regular expression literals when you use the `re.regex()` function. Note that for double quote string literals, you must escape backslash characters with backslash characters, which can look awkward.
1594
1595For example, the following regular expressions are equivalent:
1596
1597* `re.regex($e.network.email.from, `.*altostrat\.com`)`
1598* `re.regex($e.network.email.from, ".*altostrat\\.com")`
1599* `$e.network.email.from = /.*altostrat\.com/`
1600
1601Google recommends using back quote characters for strings in regular expressions for ease of readability.
1602
1603## Operators
1604
1605You can use the following operators in YARA-L:
1606
1607| | |
1608| --- | --- |
1609| **Operator** | **Description** |
1610| = | equal/declaration |
1611| != | not equal |
1612| < | less than |
1613| <= | less than or equal |
1614| > | greater than |
1615| >= | greater than or equal |
1616
1617## Variables
1618
1619In YARA-L 2.0, all variables are represented as `$<variable name>`.
1620
1621You can define the following types of variables:
1622
1623* Event variables — Represent groups of events in normalized form (UDM) or entity events. Specify conditions for event variables in the `events` section. You identify event variables using a name, event source, and event fields. Allowed sources are `udm` (for normalized events) and `graph` (for entity events). If the source is omitted, `udm` is set as the default source. Event fields are represented as a chain of *.<field name>* (for example, *$e.field1.field2*). Event field chains always start from the top-level source (UDM or Entity).
1624* Match variables — Declare in the `match` section. Match variables become grouping fields for the query, as one row is returned for each unique set of match variables (and for each time window). When the rule finds a match, the match variable values are returned. Specify what each match variable represents in the `events` section.
1625* Placeholder variables — Declare and define in the `events` section. Placeholder variables are similar to match variables. However, you can use placeholder variables in the `condition` section to specify match conditions.
1626
1627**Note:** Every placeholder variable **must** be mapped to an event field. For example, if you only referenced the following placeholder in this single line in a rule, it would fail to compile since *$var* is not bound to an event variable: *$e.field != $var*
1628
1629Use match variables and placeholder variables to declare relationships between event fields through transitive join conditions (see [Events Section Syntax](#events_section_syntax) for more detail).
1630
1631## Keywords
1632
1633Keywords in YARA-L 2.0 are case-insensitive. For example, `and` or `AND` are
1634equivalent. Variable names must not conflict with keywords. For example,
1635`$AND` or `$outcome` is invalid.
1636
1637The following are keywords for detection engine rules: `rule`, `meta`, `match`, `over`, `events`, `condition`, `outcome`, `options`, `and`, `or`, `not`, `nocase`, `in`, `regex`, `cidr`, `before`, `after`, `all`, `any`, `if`, `max`, `min`, `sum`, `array`, `array_distinct`, `count`, `count_distinct`, `is`, and `null`.
1638
1639### Maps
1640
1641YARA-L supports map access for Structs and Labels.
1642
1643#### Structs and Labels
1644
1645Some UDM fields use either the [Struct](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#struct) or [Label](/chronicle/docs/reference/udm-field-list#label) data type.
1646
1647To search for a specific key-value pair in both Struct and Label, use the standard map syntax:
1648
1649```
1650// A Struct field.
1651$e.udm.additional.fields["pod_name"] = "kube-scheduler"
1652// A Label field.
1653$e.metadata.ingestion_labels["MetadataKeyDeletion"] = "startup-script"
1654
1655```
1656
1657The map access always returns a string.
1658
1659#### Supported cases
1660
1661##### Events and Outcome Section
1662
1663```
1664// Using a Struct field in the events section
1665events:
1666 $e.udm.additional.fields["pod_name"] = "kube-scheduler"
1667
1668// Using a Label field in the outcome section
1669outcome:
1670 $value = array_distinct($e.metadata.ingestion_labels["MetadataKeyDeletion"])
1671
1672```
1673
1674##### Assigning a map value to a Placeholder
1675
1676```
1677$placeholder = $u1.metadata.ingestion_labels["MetadataKeyDeletion"]
1678
1679```
1680
1681##### Using a map field in a join condition
1682
1683```
1684// using a Struct field in a join condition between two udm events $u1 and $u2
1685$u1.metadata.event_type = $u2.udm.additional.fields["pod_name"]
1686
1687```
1688
1689#### Unsupported cases
1690
1691Maps are not supported in the following cases.
1692
1693##### Combining `any` or `all` keywords with a map
1694
1695For example, the following is not supported:
1696
1697```
1698all $e.udm.additional.fields["pod_name"] = "kube-scheduler"
1699
1700```
1701
1702##### Other types of values
1703
1704The map syntax can only return a string value. In the case of
1705[Struct](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#struct)
1706data types, the map syntax can only access keys whose values are strings.
1707Accessing keys whose values are other primitive types like integers, is not possible.
1708
1709#### Duplicate value handling
1710
1711Map accesses always returns a single value. In the uncommon
1712edge case that the map access could refer to multiple values, the map
1713access will deterministically return the first value.
1714
1715This can happen in either of the following cases:
1716
1717* A label has a duplicate key.
1718
1719 The label structure represents a map, but does not enforce key uniqueness.
1720 By convention, a map should have unique keys, so Google SecOps does
1721 not recommend populating a label with duplicate keys.
1722
1723 The rule text `$e.metadata.ingestion_labels["dupe-key"]` would return
1724 the first possible value, `val1`, if run over the following data example:
1725
1726 ```
1727 // Disrecommended usage of label with a duplicate key:
1728 event {
1729 metadata{
1730 ingestion_labels{
1731 key: "dupe-key"
1732 value: "val1" // This is the first possible value for "dupe-key"
1733 }
1734 ingestion_labels{
1735 key: "dupe-key"
1736 value: "val2"
1737 }
1738 }
1739 }
1740
1741 ```
1742* A label has an ancestor repeated field.
1743
1744 A repeated field might contain a label as a child field. Two different
1745 entries in the top-level repeated field might contain labels that
1746 have the same key. The rule text `$e.security_result.rule_labels["key"]`
1747 would return the first possible value, `val3`, if run over the following
1748 data example:
1749
1750 ```
1751 event {
1752 // security_result is a repeated field.
1753 security_result {
1754 threat_name: "threat1"
1755 rule_labels {
1756 key: "key"
1757 value: "val3" // This is the first possible value for "key"
1758 }
1759 }
1760 security_result {
1761 threat_name: "threat2"
1762 rule_labels {
1763 key: "key"
1764 value: "val4"
1765 }
1766 }
1767 }
1768
1769 ```
1770
1771## Functions
1772
1773This section describes the YARA-L 2.0 functions that you can use in detection
1774engine rules and search.
1775
1776**Note:** The use of the event variable `$e` is optional when YARA-L is used in
1777search. Both `principal.hostname` and `$e.principal.hostname` are supported in
1778search.
1779
1780These functions can be used in the following parts of a YARA-L rule:
1781
1782* `events` section.
1783* `BOOL_CLAUSE` of a conditional in the [outcome section](#outcome_section_syntax).
1784
1785### arrays.concat
1786
1787Supported in:
1788
1789[Rules](/chronicle/docs/detection/default-rules)
1790[Search](/chronicle/docs/investigation/udm-search)
1791
1792```
1793arrays.concat(string_array, string_array)
1794
1795```
1796
1797#### Description
1798
1799Returns a new string array by copying elements from original string arrays.
1800
1801#### Param data types
1802
1803`ARRAY_STRINGS`, `ARRAY_STRINGS`
1804
1805#### Return type
1806
1807`ARRAY_STRINGS`
1808
1809#### Code samples
1810
1811##### Example 1
1812
1813The following example concatenates two different string arrays.
1814
1815```
1816arrays.concat(["test1", "test2"], ["test3"]) = ["test1", "test2", "test3"]
1817
1818```
1819
1820##### Example 2
1821
1822The following example concatenates arrays with empty string.
1823
1824```
1825arrays.concat([""], [""]) = ["", ""]
1826
1827```
1828
1829##### Example 3
1830
1831The following example concatenates empty arrays.
1832
1833```
1834arrays.concat([], []) = []
1835
1836```
1837
1838
1839
1840### arrays.join\_string
1841
1842Supported in:
1843
1844[Rules](/chronicle/docs/detection/default-rules)
1845[Search](/chronicle/docs/investigation/udm-search)
1846
1847```
1848arrays.join_string(array_of_strings, optional_delimiter)
1849
1850```
1851
1852#### Description
1853
1854Converts an array of strings into a single string separated by the optional parameter. If no delimiter is provided, the empty string is used.
1855
1856#### Param data types
1857
1858`ARRAY_STRINGS`, `STRING`
1859
1860#### Return type
1861
1862`STRING`
1863
1864#### Code samples
1865
1866Here are some examples of how to use the function:
1867
1868##### Example 1
1869
1870This example joins an array with non-null elements and a delimiter.
1871
1872```
1873arrays.join_string(["foo", "bar"], ",") = "foo,bar"
1874
1875```
1876
1877##### Example 2
1878
1879This example joins an array with a null element and a delimiter.
1880
1881```
1882arrays.join_string(["foo", NULL, "bar"], ",") = "foo,bar"
1883
1884```
1885
1886##### Example 3
1887
1888This example joins an array with non-null elements and no delimiter.
1889
1890```
1891arrays.join_string(["foo", "bar"]) = "foobar"
1892
1893```
1894
1895
1896
1897### arrays.length
1898
1899Supported in:
1900
1901[Rules](/chronicle/docs/detection/default-rules)
1902[Search](/chronicle/docs/investigation/udm-search)
1903
1904```
1905arrays.length(repeatedField)
1906
1907```
1908
1909#### Description
1910
1911Returns the number of repeated field elements.
1912
1913#### Param data types
1914
1915`LIST`
1916
1917#### Return type
1918
1919`NUMBER`
1920
1921#### Code samples
1922
1923##### Example 1
1924
1925Returns the number of repeated field elements.
1926
1927```
1928arrays.length($e.principal.ip) = 2
1929
1930```
1931
1932##### Example 2
1933
1934If multiple repeated fields are along the path, returns the total number of repeated field elements.
1935
1936```
1937arrays.length($e.intermediary.ip) = 3
1938
1939```
1940
1941
1942
1943### arrays.max
1944
1945Supported in:
1946
1947[Rules](/chronicle/docs/detection/default-rules)
1948[Search](/chronicle/docs/investigation/udm-search)
1949
1950```
1951arrays.max(array_of_ints_or_floats)
1952
1953```
1954
1955#### Description
1956
1957Returns the greatest element in an array or zero if the array is empty.
1958
1959#### Param data types
1960
1961`ARRAY_INTS|ARRAY_FLOATS`
1962
1963#### Return type
1964
1965`FLOAT`
1966
1967#### Code samples
1968
1969Here are some examples of how to use the function:
1970
1971##### Example 1
1972
1973This example returns the greater element in an array of integers.
1974
1975```
1976arrays.max([10, 20]) = 20.000000
1977
1978```
1979
1980##### Example 2
1981
1982This example returns the greater element in an array of floats.
1983
1984```
1985arrays.max([10.000000, 20.000000]) = 20.000000
1986
1987```
1988
1989
1990
1991### arrays.min
1992
1993Supported in:
1994
1995[Rules](/chronicle/docs/detection/default-rules)
1996[Search](/chronicle/docs/investigation/udm-search)
1997
1998```
1999arrays.min(array_of_ints_or_floats[, ignore_zeros=false])
2000
2001```
2002
2003#### Description
2004
2005Returns the smallest element in an array or zero if the array is empty. If the
2006second, optional argument is set to true, elements equal to zero are ignored.
2007
2008#### Param data types
2009
2010`ARRAY_INTS|ARRAY_FLOATS`, `BOOL`
2011
2012#### Return type
2013
2014`FLOAT`
2015
2016#### Code samples
2017
2018Here are some examples of how to use the function:
2019
2020##### Example 1
2021
2022This example returns the smallest element in an array of integers.
2023
2024```
2025arrays.min([10, 20]) = 10.000000
2026
2027```
2028
2029##### Example 2
2030
2031This example returns the smallest element in an array of floats.
2032
2033```
2034arrays.min([10.000000, 20.000000]) = 10.000000
2035
2036```
2037
2038##### Example 3
2039
2040This example returns the smallest element in an array of floats, while ignoring the zeroes.
2041
2042```
2043arrays.min([10.000000, 20.000000, 0.0], true) = 10.000000
2044
2045```
2046
2047
2048
2049### arrays.size
2050
2051Supported in:
2052
2053[Rules](/chronicle/docs/detection/default-rules)
2054[Search](/chronicle/docs/investigation/udm-search)
2055
2056```
2057arrays.size( array )
2058
2059```
2060
2061#### Description
2062
2063Returns the size of the array. Returns 0 for an empty array.
2064
2065#### Param data types
2066
2067`ARRAY_STRINGS|ARRAY_INTS|ARRAY_FLOATS`
2068
2069#### Return type
2070
2071`INT`
2072
2073#### Code samples
2074
2075##### Example 1
2076
2077This example uses a string array that contains two elements.
2078
2079```
2080arrays.size(["test1", "test2"]) = 2
2081
2082```
2083
2084##### Example 2
2085
2086This example uses an int array that contains 3 elements.
2087
2088```
2089arrays.size([1, 2, 3]) = 3
2090
2091```
2092
2093##### Example 3
2094
2095This example uses a float array thats contains 1 elements
2096
2097```
2098arrays.size([1.200000]) = 1
2099
2100```
2101
2102##### Example 4
2103
2104This example uses an empty array.
2105
2106```
2107arrays.size([]) = 0
2108
2109```
2110
2111
2112
2113### arrays.index\_to\_float
2114
2115Supported in:
2116
2117[Rules](/chronicle/docs/detection/default-rules)
2118[Search](/chronicle/docs/investigation/udm-search)
2119
2120```
2121arrays.index_to_float(array, index)
2122
2123```
2124
2125#### Description
2126
2127Returns the element at the given index of an array. The element at that index is returned as a float.
2128
2129The index is an integer value which represents the position of an element in the array.
2130By default, the first element of an array has an index of 0, and the last element has an index of n-1, where n is the size of the array.
2131Negative indexing allows accessing array elements relative to the end of the array. For example, an index of -1 refers to the last element in the array and an index of -2 refers to the second to last element in the array.
2132
2133#### Param data types
2134
2135`ARRAY_STRINGS|ARRAY_INTS|ARRAY_FLOATS`, `INT`
2136
2137#### Return type
2138
2139`FLOAT`
2140
2141#### Code samples
2142
2143##### Example 1
2144
2145The following example fetches an element at index 1 from an array of floats.
2146
2147```
2148arrays.index_to_float([1.2, 2.1, 3.5, 4.6], 1) // 2.1
2149
2150```
2151
2152##### Example 2
2153
2154The following example fetches an element at index -1 from an array of floats.
2155
2156```
2157arrays.index_to_float([1.2, 2.1, 3.5, 4.6], 0-1) // 4.6
2158
2159```
2160
2161##### Example 3
2162
2163The following example fetches an element for an index greater than the size of the array.
2164
2165```
2166arrays.index_to_float([1.2, 2.1, 3.5, 4.6], 6) // 0.0
2167
2168```
2169
2170##### Example 4
2171
2172The following example fetches an element from an empty array.
2173
2174```
2175arrays.index_to_float([], 0) // 0.0
2176
2177```
2178
2179##### Example 5
2180
2181The following example fetches an element at index 1 from a string array.
2182
2183```
2184arrays.index_to_float(["1.2", "3.3", "2.4"], 1) // 3.3
2185
2186```
2187
2188##### Example 6
2189
2190The following example fetches an element at index 2 from an array of integers.
2191
2192```
2193arrays.index_to_float([1, 3, 2], 2) // 2.0
2194
2195```
2196
2197
2198
2199### arrays.index\_to\_int
2200
2201Supported in:
2202
2203[Rules](/chronicle/docs/detection/default-rules)
2204[Search](/chronicle/docs/investigation/udm-search)
2205
2206```
2207arrays.index_to_int(array_of_inputs, index)
2208
2209```
2210
2211#### Description
2212
2213Returns the value at a given index in an array as an integer.
2214
2215The index is an integer value which represents the position of an element in the array.
2216By default, the first element of an array has an index of 0, and the last element has an index of n-1, where n is the size of the array.
2217Negative indexing allows accessing array elements relative to the end of the array. For example, an index of -1 refers to the last element in the array and an index of -2 refers to the second to last element in the array.
2218
2219#### Param data types
2220
2221`ARRAY_STRINGS|ARRAY_INTS|ARRAY_FLOATS`, `INT`
2222
2223#### Return type
2224
2225`INT`
2226
2227#### Code samples
2228
2229##### Example 1
2230
2231This function call returns 0 when the value at the index is a non-numeric string.
2232
2233```
2234arrays.index_to_int(["str0", "str1", "str2"], 1) = 0
2235
2236```
2237
2238##### Example 2
2239
2240This function returns the element at index -1.
2241
2242```
2243arrays.index_to_int(["44", "11", "22", "33"], 0-1) = 33
2244
2245```
2246
2247##### Example 3
2248
2249Returns 0 for the out-of-bounds element.
2250
2251```
2252arrays.index_to_int(["44", "11", "22", "33"], 5) = 0
2253
2254```
2255
2256##### Example 4
2257
2258This function fetches the element from the float array at index 1.
2259
2260```
2261arrays.index_to_int([1.100000, 1.200000, 1.300000], 1) = 1
2262
2263```
2264
2265##### Example 5
2266
2267This function fetches the element from the int array at index 0.
2268
2269```
2270arrays.index_to_int([1, 2, 3], 0) = 1
2271
2272```
2273
2274
2275
2276### arrays.index\_to\_str
2277
2278Supported in:
2279
2280[Rules](/chronicle/docs/detection/default-rules)
2281[Search](/chronicle/docs/investigation/udm-search)
2282
2283```
2284arrays.index_to_str(array, index)
2285
2286```
2287
2288#### Description
2289
2290Returns the element at the given index from the array as a string.
2291The index is an integer value that represents the position of an element in the array.
2292By default, the first element of an array has an index of 0, and the last element has an index of n-1, where n is the size of the array.
2293Negative indexing allows accessing array elements from the end of the array. For example, an index of -1 refers to the last element in the array and an index of -2 refers to the second to last element in the array.
2294
2295#### Param data types
2296
2297`ARRAY_STRINGS|ARRAY_INTS|ARRAY_FLOATS`, `INT`
2298
2299#### Return type
2300
2301`STRING`
2302
2303#### Code samples
2304
2305##### Example 1
2306
2307The following example fetches an element at index 1 from an array of strings.
2308
2309```
2310arrays.index_to_str(["test1", "test2", "test3", "test4"], 1) // "test2"
2311
2312```
2313
2314##### Example 2
2315
2316The following example fetches an element at index -1 (last element of the array)
2317from an array of strings.
2318
2319```
2320arrays.index_to_str(["test1", "test2", "test3", "test4"], 0-1) // "test4"
2321
2322```
2323
2324##### Example 3
2325
2326The following example fetches an element for an index greater than the size of the array, which returns an empty string.
2327
2328```
2329arrays.index_to_str(["test1", "test2", "test3", "test4"], 6) // ""
2330
2331```
2332
2333##### Example 4
2334
2335The following example fetches an element from an empty array.
2336
2337```
2338arrays.index_to_str([], 0) // ""
2339
2340```
2341
2342##### Example 5
2343
2344The following example fetches an element at index 0 from an array of floats. The output is returned as a string.
2345
2346```
2347arrays.index_to_str([1.200000, 3.300000, 2.400000], 0) // "1.2"
2348
2349```
2350
2351##### Example 6
2352
2353The following example fetches an element at index 2 from an array of integers. The output is in the form of a string.
2354
2355```
2356arrays.index_to_str([1, 3, 2], 2) // "2"
2357
2358```
2359
2360
2361
2362### cast.as\_bool
2363
2364Supported in:
2365
2366[Rules](/chronicle/docs/detection/default-rules)
2367[Search](/chronicle/docs/investigation/udm-search)
2368
2369```
2370cast.as_bool(string_or_int)
2371
2372```
2373
2374#### Description
2375
2376Function converts an int or string value into a bool value. Function calls with
2377values that cannot be casted will return FALSE. Returns TRUE only for integer 1
2378and case insensitive string 'true'.
2379
2380#### Param data types
2381
2382`INT|STRING`
2383
2384#### Return type
2385
2386`BOOL`
2387
2388#### Code samples
2389
2390##### Example 1
2391
2392This example shows how to cast a non-boolean string
2393
2394```
2395cast.as_bool("123") = false
2396
2397```
2398
2399##### Example 2
2400
2401Truthy integer (1)
2402
2403```
2404cast.as_bool(1) = true
2405
2406```
2407
2408##### Example 3
2409
2410Truthy string
2411
2412```
2413cast.as_bool("true") = true
2414
2415```
2416
2417##### Example 4
2418
2419Capital truthy string
2420
2421```
2422cast.as_bool("TRUE") = true
2423
2424```
2425
2426##### Example 5
2427
2428Negative integer
2429
2430```
2431cast.as_bool(0-1) = false
2432
2433```
2434
2435##### Example 6
2436
2437False integer (0)
2438
2439```
2440cast.as_bool(0) = false
2441
2442```
2443
2444##### Example 7
2445
2446empty string
2447
2448```
2449cast.as_bool("") = false
2450
2451```
2452
2453
2454
2455### cast.as\_float
2456
2457Supported in:
2458
2459[Rules](/chronicle/docs/detection/default-rules)
2460[Search](/chronicle/docs/investigation/udm-search)
2461
2462```
2463cast.as_float(string_to_cast)
2464
2465```
2466
2467#### Description
2468
2469Converts a numeric string into a float. Any function calls with values that
2470cannot be casted return 0. Floats maintain precision up to 7 decimal digits.
2471
2472#### Param data types
2473
2474`STRING`
2475
2476#### Return type
2477
2478`FLOAT`
2479
2480#### Code samples
2481
2482##### Example 1
2483
2484Casting a non-numeric string returns 0.
2485
2486```
2487cast.as_float("str") = 0.0000000
2488
2489```
2490
2491##### Example 2
2492
2493Casting an empty string returns 0.
2494
2495```
2496cast.as_float("") = 0.0000000
2497
2498```
2499
2500##### Example 3
2501
2502Casting a valid numeric string returns a float value.
2503
2504```
2505cast.as_float("1.012345678") = 1.0123456
2506
2507```
2508
2509
2510
2511### cast.as\_string
2512
2513Supported in:
2514
2515[Rules](/chronicle/docs/detection/default-rules)
2516[Search](/chronicle/docs/investigation/udm-search)
2517
2518```
2519cast.as_string(int_or_bytes_or_bool, optional_default_string)
2520
2521```
2522
2523#### Description
2524
2525The `cast.as_string` function transforms an `INT`, `BYTES`, or `BOOL` value into its string representation. You can provide an optional `default_string` argument to handle cases where the cast fails. If you omit the `default_string` argument, or if the input is an invalid `UTF-8` or `BASE64` byte sequence, the function returns an empty string.
2526
2527#### Param data types
2528
2529`INT|BYTES|BOOL`, `STRING`
2530
2531#### Return type
2532
2533`STRING`
2534
2535#### Code samples
2536
2537##### Integer to String Conversion
2538
2539The function converts the integer `123` to the string `"123"`.
2540
2541```
2542cast.as_string(123) = "123"
2543
2544```
2545
2546##### Float to String Conversion
2547
2548The function converts the float `2.25` to the string `"2.25"`.
2549
2550```
2551cast.as_string(2.25) = "2.25"
2552
2553```
2554
2555##### Bytes to String Conversion
2556
2557The function converts the raw binary `b'01` to the string `"\x01"`.
2558
2559```
2560cast.as_string(b'01, "") = "\x01"
2561
2562```
2563
2564##### Boolean to String Conversion
2565
2566The function converts the boolean `true` to the string `"true"`.
2567
2568```
2569cast.as_string(true, "") = "true"
2570
2571```
2572
2573##### Failed Conversion (Defaults to the Optionally Provided String)
2574
2575The function defaults to the string `"casting error"` when the value provided is invalid.
2576
2577```
2578cast.as_string(9223372036854775808, "casting error") = "casting error"
2579
2580```
2581
2582
2583
2584### fingerprint
2585
2586Supported in:
2587
2588[Rules](/chronicle/docs/detection/default-rules)
2589
2590```
2591hash.fingerprint2011(byteOrString)
2592
2593```
2594
2595#### Description
2596
2597This function calculates the `fingerprint2011` hash of an input byte sequence
2598or string. This function returns an unsigned `INT` value in the range `[2, 0xFFFFFFFFFFFFFFFF]`.
2599
2600**Note:** This function shouldn't be used as a cryptographic secure hash.
2601
2602#### Param data types
2603
2604`BTYE`, `STRING`
2605
2606#### Return type
2607
2608`INT`
2609
2610#### Code sample
2611
2612```
2613id_fingerprint = hash.fingerprint2011("user123")
2614
2615```
2616
2617
2618
2619### group
2620
2621Supported in:
2622
2623[Search](/chronicle/docs/investigation/udm-search)
2624
2625```
2626group(field1, field2, field3, ...)
2627
2628```
2629
2630#### Description
2631
2632Group fields of a similar type into a placeholder variable.
2633
2634In UDM search, [grouped
2635fields](/chronicle/docs/investigation/udm-search#search_grouped_fields) are used to search across multiple fields of a similar type. The group
2636function is similar to grouped fields except that it lets you select which fields you want
2637grouped together to trigger a detection. You can use the group function for gathering information about a specific entity (for example, a hostname, IP address, or userid) across different [Noun types](/chronicle/docs/reference/udm-field-list#noun).
2638
2639**Note:** For search, you can use grouped fields in the events section, but not in
2640the match and outcome sections.
2641
2642#### Code samples
2643
2644**Example 1**
2645
2646Group all the IP addresses together and provide a descending count of the most prevalent IP address in the time range scanned.
2647
2648```
2649$ip = group(principal.ip, about.ip, target.ip)
2650$ip != ""
2651match:
2652 $ip
2653outcome:
2654 $count = count_distinct(metadata.id)
2655order:
2656 $count desc
2657
2658```
2659
2660
2661
2662### hash.sha256
2663
2664Supported in:
2665
2666[Rules](/chronicle/docs/detection/default-rules)
2667
2668```
2669hash.sha256(string)
2670
2671```
2672
2673#### Description
2674
2675Returns a SHA-256 hash of the input string.
2676
2677#### Param data types
2678
2679`STRING`
2680
2681#### Return type
2682
2683`STRING`
2684
2685#### Code samples
2686
2687##### Example 1
2688
2689This example shows the SHA-256 hash when the input is a valid string.
2690
2691```
2692hash.sha256("str") = "8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a"
2693
2694```
2695
2696##### Example 2
2697
2698This example shows the SHA-256 hash when the input is an empty string.
2699
2700```
2701hash.sha256("") = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
2702
2703```
2704
2705
2706
2707### math.abs
2708
2709Supported in:
2710
2711[Rules](/chronicle/docs/detection/default-rules)
2712[Search](/chronicle/docs/investigation/udm-search)
2713
2714```
2715math.abs(numericExpression)
2716
2717```
2718
2719#### Description
2720
2721Returns the absolute value of an integer or float expression.
2722
2723#### Param data types
2724
2725`NUMBER`
2726
2727#### Return type
2728
2729`NUMBER`
2730
2731#### Code samples
2732
2733##### Example 1
2734
2735This example returns True if the event was more than 5 minutes from the time
2736specified (in seconds from the Unix epoch), regardless of whether the event came
2737before or after the time specified. A call to `math.abs` cannot depend on
2738multiple variables or placeholders. For example, you cannot replace the
2739hardcoded time value of 1643687343 in the following example with
2740`$e2.metadata.event_timestamp.seconds`.
2741
2742```
2743300 < math.abs($e1.metadata.event_timestamp.seconds - 1643687343)
2744
2745```
2746
2747
2748
2749### math.ceil
2750
2751Supported in:
2752
2753[Rules](/chronicle/docs/detection/default-rules)
2754[Search](/chronicle/docs/investigation/udm-search)
2755
2756```
2757math.ceil(number)
2758
2759```
2760
2761#### Description
2762
2763Returns the smallest integer that is not less than the given number (rounding up). Will return 0 if the input is null or too big to fit in an int64.
2764
2765#### Param data types
2766
2767`FLOAT`
2768
2769#### Return type
2770
2771`INT`
2772
2773#### Code samples
2774
2775This section contains examples of using `math.ceil`.
2776
2777##### Example 1
2778
2779This example returns the ceil of a whole number.
2780
2781```
2782math.ceil(2.000000) = 2
2783
2784```
2785
2786##### Example 2
2787
2788This example returns the ceil of a negative number.
2789
2790```
2791math.ceil(0-1.200000) = -1
2792
2793```
2794
2795##### Example 3
2796
2797This example returns 0 as the ceil of a number that is too big for a 64 bit integer.
2798
2799```
2800math.ceil(184467440737095516160.0) = 0
2801
2802```
2803
2804
2805
2806### math.floor
2807
2808Supported in:
2809
2810[Rules](/chronicle/docs/detection/default-rules)
2811[Search](/chronicle/docs/investigation/udm-search)
2812
2813```
2814math.floor(float_val)
2815
2816```
2817
2818#### Description
2819
2820Returns the largest integer value that is not greater than the supplied value (rounding down). Returns 0 if the input is null or too large to fit into an int64.
2821
2822#### Param data types
2823
2824`FLOAT`
2825
2826#### Return type
2827
2828`INT`
2829
2830#### Code samples
2831
2832##### Example 1
2833
2834This example shows a positive number case.
2835
2836```
2837math.floor(1.234568) = 1
2838
2839```
2840
2841##### Example 2
2842
2843This example shows a negative number case.
2844
2845```
2846math.floor(0-1.234568) = -2
2847
2848```
2849
2850##### Example 3
2851
2852This example shows a zero case.
2853
2854```
2855math.floor(0.000000) = 0
2856
2857```
2858
2859
2860
2861### math.geo\_distance
2862
2863Supported in:
2864
2865[Rules](/chronicle/docs/detection/default-rules)
2866
2867```
2868math.geo_distance(longitude1, latitude1, longitude2, latitude2))
2869
2870```
2871
2872#### Description
2873
2874Returns the distance between two geographic locations (coordinates) in meters.
2875Returns -1 if the coordinates are invalid.
2876
2877#### Parameter data types
2878
2879`FLOAT`, `FLOAT`, `FLOAT`, `FLOAT`
2880
2881#### Return type
2882
2883`FLOAT`
2884
2885#### Code samples
2886
2887##### Example 1
2888
2889The following example returns the distance when all parameters are valid
2890coordinates:
2891
2892```
2893math.geo_distance(-122.020287, 37.407574, -122.021810, 37.407574) = 134.564318
2894
2895```
2896
2897##### Example 2
2898
2899The following example returns the distance when one of the parameters is a
2900truncated coordinate:
2901
2902```
2903math.geo_distance(-122.000000, 37.407574, -122.021810, 37.407574) = 1926.421905
2904
2905```
2906
2907##### Example 3
2908
2909The following example returns `-1` when one of the parameters is an invalid
2910coordinate:
2911
2912```
2913math.geo_distance(0-122.897680, 37.407574, 0-122.021810, 97.407574) = -1.000000
2914
2915```
2916
2917##### Example 4
2918
2919The following example returns `0` when coordinates are the same:
2920
2921```
2922math.geo_distance(-122.897680, 37.407574, -122.897680, 37.407574) = 0.000000
2923
2924```
2925
2926
2927
2928### math.is\_increasing
2929
2930Supported in:
2931
2932[Rules](/chronicle/docs/detection/default-rules)
2933[Search](/chronicle/docs/investigation/udm-search)
2934
2935```
2936math.is_increasing(num1, num2, num3)
2937
2938```
2939
2940#### Description
2941
2942Takes a list of numeric values (integers or doubles) and returns `True` if
2943the values are in ascending order, and `False` otherwise.
2944
2945#### Param data types
2946
2947`INT|FLOAT`, `INT|FLOAT`, `INT|FLOAT`
2948
2949#### Return type
2950
2951`BOOL`
2952
2953#### Code samples
2954
2955##### Example 1
2956
2957This example includes timestamp-like values in seconds.
2958
2959```
2960math.is_increasing(1716769112, 1716769113, 1716769114) = true
2961
2962```
2963
2964##### Example 2
2965
2966This example includes one negative double, one zero INT64, and one positive INT64 values.
2967
2968```
2969math.is_increasing(-1.200000, 0, 3) = true
2970
2971```
2972
2973##### Example 3
2974
2975This example includes one negative double, one zero INT64, and one negative INT64 values.
2976
2977```
2978math.is_increasing(0-1.200000, 0, 0-3) = false
2979
2980```
2981
2982##### Example 4
2983
2984This example includes two negative doubles and one zero INT64 value.
2985
2986```
2987math.is_increasing(0-1.200000, 0-1.50000, 0) = false
2988
2989```
2990
2991##### Example 5
2992
2993This example includes one negative double and two values that are the same.
2994
2995```
2996math.is_increasing(0-1.200000, 0, 0) = false
2997
2998```
2999
3000
3001
3002### math.log
3003
3004Supported in:
3005
3006[Rules](/chronicle/docs/detection/default-rules)
3007[Search](/chronicle/docs/investigation/udm-search)
3008
3009```
3010math.log(numericExpression)
3011
3012```
3013
3014#### Description
3015
3016Returns the natural log value of an integer or float expression.
3017
3018#### Param data types
3019
3020`NUMBER`
3021
3022#### Return type
3023
3024`NUMBER`
3025
3026#### Code samples
3027
3028##### Example 1
3029
3030```
3031math.log($e1.network.sent_bytes) > 20
3032
3033```
3034
3035
3036
3037### math.pow
3038
3039Supported in:
3040
3041[Rules](/chronicle/docs/detection/default-rules)
3042[Search](/chronicle/docs/investigation/udm-search)
3043
3044```
3045math.pow(base, exponent)
3046
3047```
3048
3049#### Description
3050
3051Returns the value of the first arg raised to the power of the second arg. Returns 0 in case of overflow.
3052
3053#### Param data types
3054
3055base: `INT|FLOAT`
3056exponent: `INT|FLOAT`
3057
3058#### Return type
3059
3060`FLOAT`
3061
3062#### Code samples
3063
3064##### Example 1
3065
3066This example shows an integer case.
3067
3068```
3069math.pow(2, 2) // 4.00
3070
3071```
3072
3073##### Example 2
3074
3075This example shows a fraction base case.
3076
3077```
3078math.pow(2.200000, 3) // 10.648
3079
3080```
3081
3082##### Example 3
3083
3084This example shows a fraction base and power case.
3085
3086```
3087math.pow(2.200000, 1.200000) // 2.575771
3088
3089```
3090
3091##### Example 4
3092
3093This example shows a negative power case.
3094
3095```
3096math.pow(3, 0-3) // 0.037037
3097
3098```
3099
3100##### Example 5
3101
3102This example shows a fraction power case.
3103
3104```
3105math.pow(3, 0-1.200000) // 0.267581
3106
3107```
3108
3109##### Example 6
3110
3111This example shows a negative base case.
3112
3113```
3114math.pow(0-3, 0-3) // -0.037037
3115
3116```
3117
3118##### Example 7
3119
3120This example shows a zero base case.
3121
3122```
3123math.pow(0, 3) // 0
3124
3125```
3126
3127##### Example 8
3128
3129This example shows a zero power case.
3130
3131```
3132math.pow(9223372036854775807, 0) // 1
3133
3134```
3135
3136##### Example 9
3137
3138This example shows a large base case.
3139
3140```
3141math.pow(9223372036854775807, 1.200000) // 57262152889751593549824
3142
3143```
3144
3145
3146
3147### math.random
3148
3149Supported in:
3150
3151[Rules](/chronicle/docs/detection/default-rules)
3152[Search](/chronicle/docs/investigation/udm-search)
3153
3154```
3155math.random()
3156
3157```
3158
3159#### Description
3160
3161Generates a pseudo-random value of type DOUBLE in the range of `[0, 1)`, inclusive of 0 and exclusive of 1.
3162
3163#### Return type
3164
3165`FLOAT`
3166
3167#### Code samples
3168
3169The following example checks whether the random value is in the range `[0, 1)`.
3170`none
3171if(math.random() >= 0 and math.random() < 1) = true`
3172
3173### math.round
3174
3175Supported in:
3176
3177[Search](/chronicle/docs/investigation/udm-search)
3178
3179```
3180math.round(numericExpression, decimalPlaces)
3181
3182```
3183
3184#### Description
3185
3186Returns a value rounded to the nearest integer or to the specified number of decimal places.
3187
3188#### Param data types
3189
3190`NUMBER`
3191
3192#### Return type
3193
3194`NUMBER`
3195
3196#### Code samples
3197
3198```
3199math.round(10.7) // returns 11
3200math.round(1.2567, 2) // returns 1.25
3201math.round(0-10.7) // returns -11
3202math.round(0-1.2) // returns -1
3203math.round(4) // returns 4, math.round(integer) returns the integer
3204
3205```
3206
3207
3208
3209### math.sqrt
3210
3211Supported in:
3212
3213[Rules](/chronicle/docs/detection/default-rules)
3214[Search](/chronicle/docs/investigation/udm-search)
3215
3216```
3217math.sqrt(number)
3218
3219```
3220
3221#### Description
3222
3223Returns the square root of the given number. Returns 0 in case of negative numbers.
3224
3225#### Param data types
3226
3227`INT|FLOAT`
3228
3229#### Return type
3230
3231`FLOAT`
3232
3233#### Code samples
3234
3235##### Example 1
3236
3237This example returns the square root of an int argument.
3238
3239```
3240math.sqrt(3) = 1.732051
3241
3242```
3243
3244##### Example 2
3245
3246This example returns the square root of a negative int argument.
3247
3248```
3249math.sqrt(-3) = 0.000000
3250
3251```
3252
3253##### Example 3
3254
3255This example returns the square root of zero argument.
3256
3257```
3258math.sqrt(0) = 0.000000
3259
3260```
3261
3262##### Example 4
3263
3264This example returns the square root of a float argument.
3265
3266```
3267math.sqrt(9.223372) = 3.037000
3268
3269```
3270
3271##### Example 5
3272
3273This example returns the square root of a negative float argument.
3274
3275```
3276math.sqrt(0-1.200000) = 0.000000
3277
3278```
3279
3280
3281
3282### metrics
3283
3284Supported in:
3285
3286[Rules](/chronicle/docs/detection/default-rules)
3287
3288Metrics functions can aggregate large amounts of historical data. You can use
3289this in your rule using `metrics.functionName()` in the outcome
3290section.
3291
3292For more information, see [YARA-L Metrics](/chronicle/docs/detection/metrics-functions).
3293
3294### net.ip\_in\_range\_cidr
3295
3296Supported in:
3297
3298[Rules](/chronicle/docs/detection/default-rules)
3299[Search](/chronicle/docs/investigation/udm-search)
3300
3301```
3302net.ip_in_range_cidr(ipAddress, subnetworkRange)
3303
3304```
3305
3306#### Description
3307
3308Returns `true` when the given IP address is within the specified subnetwork.
3309
3310You can use YARA-L to search for UDM events across all of the IP addresses
3311within a subnetwork using the `net.ip_in_range_cidr()` statement.
3312Both IPv4 and IPv6 are supported.
3313
3314To search across a range of IP addresses, specify an IP UDM field and a CIDR
3315range. YARA-L can handle both singular and repeating IP address fields.
3316
3317To search across a range of IP addresses, specify an `ip` UDM field and a Classless Inter-Domain Routing (CIDR) range. YARA-L can handle both singular and repeating IP address fields.
3318
3319#### Param data types
3320
3321`STRING`, `STRING`
3322
3323#### Return type
3324
3325`BOOL`
3326
3327#### Code samples
3328
3329##### Example 1
3330
3331IPv4 example:
3332
3333```
3334net.ip_in_range_cidr($e.principal.ip, "192.0.2.0/24")
3335
3336```
3337
3338##### Example 2
3339
3340IPv6 example:
3341
3342```
3343net.ip_in_range_cidr($e.network.dhcp.yiaddr, "2001:db8::/32")
3344
3345```
3346
3347For an example rule using the `net.ip_in_range_cidr()`statement, see the example rule in [Single Event within Range of IP Addresses](/chronicle/docs/detection/yara-l-2-0-overview#single_event_within_range_of_ip_addresses).)
3348
3349### re.regex
3350
3351Supported in:
3352
3353[Rules](/chronicle/docs/detection/default-rules)
3354[Search](/chronicle/docs/investigation/udm-search)
3355
3356You can define regular expression matching in YARA-L 2.0 using either of the following syntax:
3357
3358* Using YARA-L syntax — Related to events.
3359 The following is a generic representation of this syntax:
3360
3361 ```
3362 $e.field = /regex/
3363
3364 ```
3365* Using YARA-L syntax — As a function taking in the following parameters:
3366
3367 + Field the regular expression is applied to.
3368 + Regular expression specified as a string.
3369
3370 The following is a generic representation of this syntax:
3371
3372 ```
3373 re.regex($e.field, `regex`)
3374
3375 ```
3376
3377#### Description
3378
3379This function returns `true` if the string contains a substring that matches the regular expression provided. It is unnecessary to add `.*` to the beginning or at the end of the regular expression.
3380
3381##### Notes
3382
3383* To match the exact string or only a prefix or suffix, include the `^`
3384 (starting) and `$` (ending) anchor characters in the regular expression.
3385 For example, `/^full$/` matches `"full"` exactly, while `/full/` could match
3386 `"fullest"`, `"lawfull"`, and `"joyfully"`.
3387* If the UDM field includes newline characters, the `regexp` only matches the
3388 first line of the UDM field. To enforce full UDM field matching, add a `(?s)` to
3389 the regular expression. For example, replace `/.*allUDM.*/` with
3390 `/(?s).*allUDM.*/`.
3391* You can use the `nocase` modifier after strings to indicate that the search
3392 should ignore capitalization.
3393
3394#### Param data types
3395
3396`STRING`, `STRING`
3397
3398#### Param expression types
3399
3400`ANY`, `ANY`
3401
3402#### Return type
3403
3404`BOOL`
3405
3406#### Code samples
3407
3408##### Example 1
3409
3410```
3411// Equivalent to $e.principal.hostname = /google/
3412re.regex($e.principal.hostname, "google")
3413
3414```
3415
3416
3417
3418### re.capture
3419
3420Supported in:
3421
3422[Rules](/chronicle/docs/detection/default-rules)
3423[Search](/chronicle/docs/investigation/udm-search)
3424
3425```
3426re.capture(stringText, regex)
3427
3428```
3429
3430#### Description
3431
3432Captures (extracts) data from a string using the regular expression pattern
3433provided in the argument.
3434
3435This function takes two arguments:
3436
3437* `stringText`: the original string to search.
3438* `regex`: the regular expression indicating the pattern to search for.
3439
3440The regular expression can contain 0 or 1 capture groups in parentheses. If the
3441regular expression contains 0 capture groups, the function returns the first
3442entire matching substring. If the regular expression contains 1 capture group,
3443it returns the first matching substring for the capture group. Defining two or
3444more capture groups returns a compiler error.
3445
3446#### Param data types
3447
3448`STRING`, `STRING`
3449
3450#### Return type
3451
3452`STRING`
3453
3454#### Code samples
3455
3456##### Example 1
3457
3458In this example, if `$e.principal.hostname` contains "aaa1bbaa2" the following would be true, because the function
3459returns the first instance. This example has no capture groups.
3460
3461```
3462"aaa1" = re.capture($e.principal.hostname, "a+[1-9]")
3463
3464```
3465
3466##### Example 2
3467
3468This example captures everything after the @ symbol in an email. If the
3469`$e.network.email.from` field is `test@google.com`, the example returns
3470`google.com`. The following example contains one capture group.
3471
3472```
3473"google.com" = re.capture($e.network.email.from , "@(.*)")
3474
3475```
3476
3477##### Example 3
3478
3479If the regular expression does not match any substring in the text, the
3480function returns an empty string. You can omit events where no match occurs
3481by excluding the empty string, which is especially important when you are
3482using `re.capture()` with an inequality:
3483
3484```
3485// Exclude the empty string to omit events where no match occurs.
3486"" != re.capture($e.network.email.from , "@(.*)")
3487
3488// Exclude a specific string with an inequality.
3489"google.com" != re.capture($e.network.email.from , "@(.*)")
3490
3491```
3492
3493
3494
3495### re.replace
3496
3497Supported in:
3498
3499[Rules](/chronicle/docs/detection/default-rules)
3500[Search](/chronicle/docs/investigation/udm-search)
3501
3502```
3503re.replace(stringText, replaceRegex, replacementText)
3504
3505```
3506
3507#### Description
3508
3509Performs a regular expression replacement.
3510
3511This function takes three arguments:
3512
3513* `stringText`: the original string.
3514* `replaceRegex`: the regular expression indicating the pattern to search for.
3515* `replacementText`: The text to insert into each match.
3516
3517Returns a new string derived from the original `stringText`, where all
3518substrings that match the pattern in `replaceRegex` are replaced with the value in
3519`replacementText`. You can use backslash-escaped digits (`\1` to `\9`) within
3520`replacementText` to insert text matching the corresponding parenthesized group
3521in the `replaceRegex` pattern. Use `\0` to refer to the entire matching text.
3522
3523The function replaces non-overlapping matches and will prioritize replacing the
3524first occurrence found. For example, `re.replace("banana", "ana", "111")`
3525returns the string "b111na".
3526
3527#### Param data types
3528
3529`STRING`, `STRING`, `STRING`
3530
3531#### Return type
3532
3533`STRING`
3534
3535#### Code samples
3536
3537##### Example 1
3538
3539This example captures everything after the `@` symbol in an email, replaces `com`
3540with `org`, and then returns the result. Notice the use of nested functions.
3541
3542```
3543"email@google.org" = re.replace($e.network.email.from, "com", "org")
3544
3545```
3546
3547##### Example 2
3548
3549This example uses backslash-escaped digits in the `replacementText` argument to
3550reference matches to the `replaceRegex` pattern.
3551
3552```
3553"test1.com.google" = re.replace(
3554 $e.principal.hostname, // holds "test1.test2.google.com"
3555 "test2\.([a-z]*)\.([a-z]*)",
3556 "\\2.\\1" // \\1 holds "google", \\2 holds "com"
3557 )
3558
3559```
3560
3561##### Example 3
3562
3563Note the following cases when dealing with empty strings and `re.replace()`:
3564
3565Using empty string as `replaceRegex`:
3566
3567```
3568// In the function call below, if $e.principal.hostname contains "name",
3569// the result is: 1n1a1m1e1, because an empty string is found next to
3570// every character in `stringText`.
3571re.replace($e.principal.hostname, "", "1")
3572
3573```
3574
3575To replace an empty string, you can use `"^$"` as `replaceRegex`:
3576
3577```
3578// In the function call below, if $e.principal.hostname contains the empty
3579// string, "", the result is: "none".
3580re.replace($e.principal.hostname, "^$", "none")
3581
3582```
3583
3584
3585
3586### sample\_rate
3587
3588Supported in:
3589
3590[Rules](/chronicle/docs/detection/default-rules)
3591
3592```
3593optimization.sample_rate(byteOrString, rateNumerator, rateDenominator)
3594
3595```
3596
3597#### Description
3598
3599This function determines whether to include an event based on a deterministic
3600sampling strategy. This function returns:
3601
3602* `true` for a fraction of input values, equivalent to (`rateNumerator` / `rateDenominator`),
3603 indicating that the event should be included in the sample.
3604* `false` indicating that the event shouldn't be included in the sample.
3605
3606This function is useful for optimization scenarios where you want to process
3607only a subset of events. Equivalent to:
3608
3609```
3610hash.fingerprint2011(byteOrString) % rateDenominator < rateNumerator
3611
3612```
3613
3614#### Param data types
3615
3616* byteOrString: Expression that evaluates to either a `BYTE` or `STRING`.
3617* rateNumerator: 'INT'
3618* rateDenominator: 'INT'
3619
3620#### Return type
3621
3622`BOOL`
3623
3624#### Code sample
3625
3626```
3627events:
3628 $e.metadata.event_type = "NETWORK_CONNECTION"
3629 $asset_id = $e.principal.asset.asset_id
3630 optimization.sample_rate($e.metadata.id, 1, 5) // Only 1 out of every 5 events
3631
3632 match:
3633 $asset_id over 1h
3634
3635 outcome:
3636 $event_count = count_distinct($e.metadata.id)
3637 // estimate the usage by multiplying by the inverse of the sample rate
3638 $usage_past_hour = sum(5.0 * $e.network.sent_bytes)
3639
3640 condition:
3641 // Requiring a certain number of events after sampling avoids bias (e.g. a
3642 // device with just 1 connection will still show up 20% of the time and
3643 // if we multiply that traffic by 5, we'll get an incorrect estimate)
3644 $e and ($usage_past_hour > 1000000000) and $event_count >= 100
3645
3646```
3647
3648
3649
3650### strings.base64\_decode
3651
3652Supported in:
3653
3654[Rules](/chronicle/docs/detection/default-rules)
3655[Search](/chronicle/docs/investigation/udm-search)
3656
3657```
3658strings.base64_decode(encodedString)
3659
3660```
3661
3662#### Description
3663
3664Returns a string containing the base64 decoded version of the encoded string.
3665
3666This function takes one base64 encoded string as an argument. If `encodedString`
3667is not a valid base64 encoded string, the function returns `encodedString` unchanged.
3668
3669#### Param data types
3670
3671`STRING`
3672
3673#### Return type
3674
3675`STRING`
3676
3677#### Code samples
3678
3679##### Example 1
3680
3681```
3682"test" = strings.base64_decode($e.principal.domain.name)
3683
3684```
3685
3686
3687
3688### strings.coalesce
3689
3690Supported in:
3691
3692[Rules](/chronicle/docs/detection/default-rules)
3693[Search](/chronicle/docs/investigation/udm-search)
3694
3695```
3696strings.coalesce(a, b, c, ...)
3697
3698```
3699
3700#### Description
3701
3702This function takes an unlimited number of arguments and returns the value of the first expression that does not evaluate to an empty string (for example, "non-zero value"). If all arguments evaluate to an empty string, the function call returns an empty string.
3703
3704The arguments can be literals, event fields, or function calls. All arguments must be of `STRING` type. If any arguments are event fields, the attributes must be from the same event.
3705
3706#### Param data types
3707
3708`STRING`
3709
3710#### Return type
3711
3712`STRING`
3713
3714#### Code samples
3715
3716##### Example 1
3717
3718The following example includes string variables as arguments. The condition
3719evaluates to true when (1) `$e.network.email.from` is `suspicious@gmail.com` or
3720(2) `$e.network.email.from` is empty and `$e.network.email.to` is
3721`suspicious@gmail.com`.
3722
3723```
3724"suspicious@gmail.com" = strings.coalesce($e.network.email.from, $e.network.email.to)
3725
3726```
3727
3728##### Example 2
3729
3730The following example calls the `coalesce` function with more than two
3731arguments. This condition compares the first non-null IP address from event `$e`
3732against values in the reference list `ip_watchlist`. The order that the
3733arguments are coalesced in this call is the same as the order they are
3734enumerated in the rule condition:
3735
37361. `$e.principal.ip` is evaluated first.
37372. `$e.src.ip` is evaluated next.
37383. `$e.target.ip` is evaluated next.
37394. Finally, the string "No IP" is returned as a default value if the previous `ip`
3740 fields are unset.
3741
3742```
3743strings.coalesce($e.principal.ip, $e.src.ip, $e.target.ip, "No IP") in %ip_watchlist
3744
3745```
3746
3747##### Example 3
3748
3749The following example attempts to coalesce `principal.hostname` from event
3750`$e1` and event `$e2`. It will return a compiler error because the arguments are
3751different event variables.
3752
3753```
3754// returns a compiler error
3755"test" = strings.coalesce($e1.principal.hostname, $e2.principal.hostname)
3756
3757```
3758
3759
3760
3761### strings.concat
3762
3763Supported in:
3764
3765[Rules](/chronicle/docs/detection/default-rules)
3766[Search](/chronicle/docs/investigation/udm-search)
3767
3768```
3769strings.concat(a, b, c, ...)
3770
3771```
3772
3773#### Description
3774
3775Returns the concatenation of an unlimited number of items, each of which can be
3776a string, integer, or float.
3777
3778If any arguments are event fields, the attributes must be from the same event.
3779
3780#### Param data types
3781
3782`STRING`, `FLOAT`, `INT`
3783
3784#### Return type
3785
3786`STRING`
3787
3788#### Code samples
3789
3790##### Example 1
3791
3792The following example includes a string variable and integer variable as
3793arguments. Both `principal.hostname` and `principal.port` are from the same
3794event, `$e`, and are concatenated to return a string.
3795
3796```
3797"google:80" = strings.concat($e.principal.hostname, ":", $e.principal.port)
3798
3799```
3800
3801##### Example 2
3802
3803The following example includes a string variable and string literal as arguments.
3804
3805```
3806"google-test" = strings.concat($e.principal.hostname, "-test") // Matches the event when $e.principal.hostname = "google"
3807
3808```
3809
3810##### Example 3
3811
3812The following example includes a string variable and float literal as arguments.
3813When represented as strings, floats that are whole numbers are formatted without
3814the decimal point (for example, 1.0 is represented as "1"). Additionally,
3815floats that exceed sixteen decimal digits are truncated to the sixteenth decimal
3816place.
3817
3818```
3819"google2.5" = strings.concat($e.principal.hostname, 2.5)
3820
3821```
3822
3823##### Example 4
3824
3825The following example includes a string variable, string literal,
3826integer variable, and float literal as arguments. All variables are from the
3827same event, `$e`, and are concatenated with the literals to return a string.
3828
3829```
3830"google-test802.5" = strings.concat($e.principal.hostname, "-test", $e.principal.port, 2.5)
3831
3832```
3833
3834##### Example 5
3835
3836The following example attempts to concatenate principal.port from event `$e1`,
3837with `principal.hostname` from event `$e2`. It will return a compiler error
3838because the arguments are different event variables.
3839
3840```
3841// Will not compile
3842"test" = strings.concat($e1.principal.port, $e2.principal.hostname)
3843
3844```
3845
3846
3847
3848### strings.contains
3849
3850Supported in:
3851
3852[Rules](/chronicle/docs/detection/default-rules)
3853[Search](/chronicle/docs/investigation/udm-search)
3854
3855```
3856strings.contains( str, substr )
3857
3858```
3859
3860#### Description
3861
3862Returns true if a given string contains the specified substring. Otherwise it returns false.
3863
3864#### Param data types
3865
3866`STRING`, `STRING`
3867
3868#### Return type
3869
3870`BOOL`
3871
3872#### Code samples
3873
3874##### Example 1
3875
3876This example returns true because the string has a substring "is".
3877
3878```
3879strings.contains("thisisastring", "is") = true
3880
3881```
3882
3883##### Example 2
3884
3885This example returns false because the string does not have substring "that".
3886
3887```
3888strings.contains("thisisastring", "that") = false
3889
3890```
3891
3892
3893
3894### strings.count\_substrings
3895
3896Supported in:
3897
3898[Rules](/chronicle/docs/detection/default-rules)
3899[Search](/chronicle/docs/investigation/udm-search)
3900
3901```
3902strings.count_substrings(string_to_search_in, substring_to_count)
3903
3904```
3905
3906#### Description
3907
3908When given a string and a substring, returns an int64 of the count of non-overlapping occurrences of the substring within the string.
3909
3910#### Param data types
3911
3912`STRING`, `STRING`
3913
3914#### Return type
3915
3916`INT`
3917
3918#### Code samples
3919
3920This section contains examples that calculate the number of times a substring appears in a given string.
3921
3922##### Example 1
3923
3924This example uses a non-null string and a non-null single substring character.
3925
3926```
3927strings.count_substrings("this`string`has`four`backticks", "`") = 4
3928
3929```
3930
3931##### Example 2
3932
3933This example uses a non-null string and a non-null substring greater than one character.
3934
3935```
3936strings.count_substrings("str", "str") = 1
3937
3938```
3939
3940##### Example 3
3941
3942This example uses a non-null string and an empty substring.
3943
3944```
3945strings.count_substrings("str", "") = 0
3946
3947```
3948
3949##### Example 4
3950
3951This example uses an empty string and a non-null substring greater than one character.
3952
3953```
3954strings.count_substrings("", "str") = 0
3955
3956```
3957
3958##### Example 5
3959
3960This example uses an empty string and an empty substring.
3961
3962```
3963strings.count_substrings("", "") = 0
3964
3965```
3966
3967##### Example 6
3968
3969This example uses a non-null string and a non-null substring that is greater than one character and greater than one occurrence.
3970
3971```
3972strings.count_substrings("fooABAbarABAbazABA", "AB") = 3
3973
3974```
3975
3976##### Example 7
3977
3978This example uses a non-null string and a non-null substring that is greater than one character and greater than one occurrence. It highlights the limitation with overlapping substring occurrences
3979
3980```
3981strings.count_substrings("ABABABA", "ABA") = 2
3982
3983```
3984
3985
3986
3987### strings.extract\_domain
3988
3989Supported in:
3990
3991[Rules](/chronicle/docs/detection/default-rules)
3992[Search](/chronicle/docs/investigation/udm-search)
3993
3994```
3995strings.extract_domain(url_string)
3996
3997```
3998
3999#### Description
4000
@@ −1 +1 @@
1−# .clinerules Directory Overview
2−
3−This directory contains configuration files and documentation to provide context and guidance for LLM Agents operating within this security environment.
4−
5−## Existing Components
6−
7−### Personas (`./personas/`)
8−
9−* **Purpose:** These files define standard roles within the security operations team (e.g., SOC Analyst Tiers 1-3, Incident Responder, Threat Hunter, CTI Researcher, Security Engineer, Compliance Manager, SOC Manager).
10−* **Content:** Each persona description outlines typical responsibilities, required skills, commonly used MCP tools, and relevant runbooks.
11−* **Usage by LLM Agent:** Helps the agent understand user intent, tailor responses and actions to the user's likely role and perspective, and select appropriate tools and runbooks.
12−
13−### Runbooks (`./run_books/`)
14−
15−* **Purpose:** These files contain documented, step-by-step procedures or workflows for specific security operations tasks (e.g., triaging alerts, investigating IOCs, hunting for threats, responding to phishing).
16−* **Content:** They often include objectives, scope, required inputs, specific MCP tools to use, workflow steps (sometimes visualized with diagrams like Mermaid), and expected outcomes.
17−* **Usage by LLM Agent:** Serves as a primary plan for executing common security workflows, ensuring adherence to established procedures, guiding tool selection and sequencing, and promoting consistency.
18−* **IRP vs. Runbook Distinction:** While all files here serve as procedural guides, we differentiate between:
19− * **Incident Response Plans (IRPs):** Located in the `./run_books/irps/` subdirectory, these outline the *end-to-end strategy* for handling major incident types (e.g., malware, phishing) following the full PICERL lifecycle. They orchestrate multiple steps and often call other runbooks. Use these as the starting point for major incident types.
20− * **Runbooks:** Located directly within `./run_books/` or in `./run_books/common_steps/`, these provide detailed, *tactical steps* for specific tasks (e.g., enriching an IOC, triaging an alert, isolating an endpoint) or reusable procedures. They are often components within a larger IRP.
21−
22−## Suggested Additional Context Files
23−
24−The following types of files could further enhance an LLM Agent's effectiveness:
25−
26−### 1. Environment & Infrastructure Context
27−
28−* **`network_map.md`**: Describes key network segments (e.g., DMZ, production servers, user subnets), their IP ranges, and primary functions. Helps in understanding the context of network events and potential lateral movement.
29−* **`asset_inventory_guidelines.md`**: Outlines naming conventions for hosts/servers, common OS types, and potentially maps critical assets to their roles or owners. Helps contextualize alerts involving specific hosts.
30−* **`critical_applications.md`**: Lists key business applications, their associated servers/IPs, and expected communication patterns. Useful for identifying anomalous behavior related to core services.
31−* **`cloud_architecture.md`**: Provides an overview of the cloud environment structure (e.g., GCP project organization, key services like GKE, Cloud SQL), relevant for cloud-focused investigations (using SCC, etc.).
32−
33−### 2. Tool Configuration & Usage
34−
35−* **`tool_configurations.md`**: Details specific configurations crucial for tool usage, like:
36− * Important Chronicle Reference List names (e.g., `IP_Blocklist`, `Domain_Allowlist`) and their purpose.
37− * Key SOAR playbook names/IDs and what triggers them.
38− * Default timeframes or limits preferred for certain searches.
39−* **`mcp_tool_best_practices.md`**: Offers tips or preferred syntax for using specific MCP tools effectively (e.g., optimizing `search_security_events` queries, interpreting specific GTI fields).
40−* **`tool_rate_limits.md`**: Explicitly lists known rate limits or quotas for tools (like the Chronicle UDM query limit mentioned for a SOAR action) to help manage usage.
41−
42−### 3. Organizational Policies & Procedures
43−
44−* **`incident_severity_matrix.md`**: Defines how incident severity (Low, Medium, High, Critical) is determined based on impact and threat type. Aids in prioritization.
45−* **`escalation_paths.md`**: Outlines who to notify or escalate to under specific circumstances (e.g., confirmed ransomware, PII exposure).
46−* **`reporting_templates.md`**: Provides standard formats or key sections required for different types of reports (e.g., daily SOC summary, post-incident report).
47−* **`approved_remediations.md`**: Lists standard, pre-approved containment or remediation actions for common, lower-severity findings.
48−* **`key_contacts.md`**: Lists relevant teams or individuals for specific issues (e.g., Network Ops, Identity Team, Legal).
49−
50−### 4. Threat Intelligence & Context
51−
52−* **`internal_threat_profile.md`**: Details specific threat actors, campaigns, or TTPs that are of high concern to *this specific organization*.
53−* **`allowlists.md`**: Lists organization-specific known-good IPs, domains, file hashes, or process names that should generally be ignored unless context suggests otherwise.
54−* **`common_benign_alerts.md`**: Describes alerts often triggered by known benign activity (e.g., vulnerability scans, specific admin scripts) and how to typically handle them.
55−
56−Having these additional context files would allow the LLM Agent to perform more nuanced analysis, make better-informed decisions, adhere more closely to organizational standards, and require less clarification during complex tasks.
1+
2+# YARA-L 2.0 language syntax
3+
4+Supported in:
5+
6+Google secops
7+[Siem](/chronicle/docs/secops/google-secops-siem-toc)
8+
9+This section describes the major elements of the YARA-L syntax. See also [Overview of the YARA-L 2.0 language](/chronicle/docs/detection/yara-l-2-0-overview).
10+
11+**Note:** YARA-L syntax doesn't allow negative integers. For example,
12+`$e.principal.ip[-1]` is not valid. Replace `-1` with `0-1`.
13+
14+## Rule structure
15+
16+For YARA-L 2.0, you must specify variable declarations, definitions, and usages in the following order:
17+
18+1. meta
19+2. events
20+3. match (optional)
21+4. outcome (optional)
22+5. condition
23+6. options (optional)
24+
25+**Note:** If you exclude `match`, the rule can match against a single event.
26+
27+The following example illustrates the generic structure of a rule:
28+
29+```
30+rule <rule Name>
31+{
32+ meta:
33+ // Stores arbitrary key-value pairs of rule details, such as who wrote
34+ // it, what it detects on, version control, etc.
35+
36+ events:
37+ // Conditions to filter events and the relationship between events.
38+
39+ match:
40+ // Values to return when matches are found.
41+
42+ outcome:
43+ // Additional information extracted from each detection.
44+
45+ condition:
46+ // Condition to check events and the variables used to find matches.
47+
48+ options:
49+ // Options to turn on or off while executing this rule.
50+}
51+
52+```
53+
54+## Meta section syntax
55+
56+Meta section is composed of multiple lines, where each line defines a key-value pair. A key part must be an unquoted string, and a value part must be a quoted string:
57+
58+`<key> = "<value>"`
59+
60+The following is an example of a valid `meta` section line:
61+
62+```
63+meta:
64+ author = "Google"
65+ severity = "HIGH"
66+
67+```
68+
69+## Events section syntax
70+
71+In the `events` section, list the predicates to specify the following:
72+
73+* Variable declarations
74+* Event variable filters
75+* Event variable joins
76+
77+### Variable declarations
78+
79+For variable declarations, use the following syntax:
80+
81+* `<EVENT_FIELD> = <VAR>`
82+* `<VAR> = <EVENT_FIELD>`
83+
84+Both are equivalent, as shown in the following examples:
85+
86+* `$e.source.hostname = $hostname`
87+* `$userid = $e.principal.user.userid`
88+
89+This declaration indicates that this variable represents the specified field for the event variable. When the event field is a repeated field, the match variable can represent any value in the array. It is also possible to assign multiple event fields to a single match or placeholder variable. This is a transitive join condition.
90+
91+For example, the following:
92+
93+* `$e1.source.ip = $ip`
94+* `$e2.target.ip = $ip`
95+
96+Are equivalent to:
97+
98+* `$e1.source.ip = $ip`
99+* `$e1.source.ip = $e2.target.ip`
100+
101+When a variable is used, the variable must be declared through variable declaration. If a variable is used without any declaration, it is regarded as a compilation error.
102+
103+### Event variable filters
104+
105+A [boolean expression](#boolean_expressions) that acts on a single event variable is considered a filter.
106+
107+### Event variable joins
108+
109+All event variables used in the rule must be joined with every other event variable in either of the following ways:
110+
111+* Directly through an equality comparison between event fields of the two joined event variables, for example: `$e1.field = $e2.field`. The expression must not include arithmetic.
112+* Indirectly through a transitive join involving only an event field (see [variable declaration](#variable_declarations) for a definition of "transitive join"). The expression must not include arithmetic.
113+
114+For example, assuming $e1, $e2, and $e3 are used in the rule, the following `events` sections are valid.
115+
116+```
117+events:
118+ $e1.principal.hostname = $e2.src.hostname // $e1 joins with $e2
119+ $e2.principal.ip = $e3.src.ip // $e2 joins with $e3
120+
121+```
122+
123+```
124+events:
125+ // $e1 joins with $e2 via function to event comparison
126+ re.capture($e1.src.hostname, ".*") = $e2.target.hostname
127+
128+```
129+
130+```
131+events:
132+ // $e1 joins with $e2 via an `or` expression
133+ $e1.principal.hostname = $e2.src.hostname
134+ or $e1.principal.hostname = $e2.target.hostname
135+ or $e1.principal.hostname = $e2.principal.hostname
136+
137+```
138+
139+```
140+events:
141+ // all of $e1, $e2 and $e3 are transitively joined via the placeholder variable $ip
142+ $e1.src.ip = $ip
143+ $e2.target.ip = $ip
144+ $e3.about.ip = $ip
145+
146+```
147+
148+```
149+events:
150+ // $e1 and $e2 are transitively joined via function to event comparison
151+ re.capture($e2.principal.application, ".*") = $app
152+ $e1.principal.hostname = $app
153+
154+```
155+
156+**Note:** If your sole join condition is an `or` chain, a function to event
157+comparison, or a combination of both, then the rule may perform poorly.
158+
159+However, here are examples of invalid `events` sections.
160+
161+```
162+events:
163+ // Event to arithmetic comparison is an invalid join condition for $e1 and $e2.
164+ $e1.principal.port = $e2.src.port + 1
165+
166+```
167+
168+```
169+events:
170+ $e1.src.ip = $ip
171+ $e2.target.ip = $ip
172+ $e3.about.ip = "192.1.2.0" //$e3 is not joined with $e1 or $e2.
173+
174+```
175+
176+```
177+events:
178+ $e1.src.port = $port
179+
180+ // Arithmetic to placeholder comparison is an invalid transitive join condition.
181+ $e2.principal.port + 800 = $port
182+
183+```
184+
185+## Match section syntax
186+
187+In the `match` section, list the match variables for group events before checking for match conditions. Those fields are returned with each match.
188+
189+* Specify what each match variable represents in the `events` section.
190+* Specify the time duration to use to correlate events after the `over` keyword. Events outside the time duration are ignored.
191+* Use the following syntax to specify the time duration: `<number><m/h/d>`
192+
193+ Where `m/h/d` means minutes, hours, and days respectively.
194+* Minimum time you can specify is 1 minute.
195+* Maximum time you can specify is 48 hours.
196+
197+The following is an example of a valid `match`:
198+
199+```
200+$var1, $var2 over 5m
201+
202+```
203+
204+This statement returns `$var1` and `$var2` (defined in the `events` section) when the rule finds a match. The time specified is 5 minutes. Events that are more than 5 minutes apart are not correlated and therefore ignored by the rule.
205+
206+Here is another example of a valid `match` section:
207+
208+```
209+$user over 1h
210+
211+```
212+
213+This statement returns `$user` when the rule finds a match. The time window specified is 1 hour. Events that are more than an hour apart are not correlated. The rule does not consider them to be a detection.
214+
215+Here is another example of a valid `match` section:
216+
217+```
218+$source_ip, $target_ip, $hostname over 2m
219+
220+```
221+
222+This statement returns `$source_ip`, `$target_ip`, and `$hostname` when the rule finds a match. The time window specified is 2 minutes. Events that are more than 2 minutes apart are not correlated. The rule does not consider them to be a detection.
223+
224+The following examples illustrate **invalid** `match` sections:
225+
226+* `var1, var2 over 5m // invalid variable name`
227+* `$user 1h // missing keyword`
228+
229+### Zero value handling in the match section
230+
231+Rules Engine implicitly filters out the zero values for all placeholders that
232+are used in the match section (`""` for
233+string, `0` for numbers, `false` for booleans, the value in position 0
234+for [enumerated types](/chronicle/docs/reference/udm-field-list#event_enumerated_types)).
235+The following example illustrates rules that filter out the zero values.
236+
237+```
238+rule ZeroValuePlaceholderExample {
239+ meta:
240+ events:
241+ // Because $host is used in the match section, the rule behaves
242+ // as if the following predicate was added to the events section:
243+ // $host != ""
244+ $host = $e.principal.hostname
245+
246+ // Because $otherPlaceholder was not used in the match section,
247+ // there is no implicit filtering of zero values for $otherPlaceholder.
248+ $otherPlaceholder = $e.principal.ip
249+
250+ match:
251+ $host over 5m
252+
253+ condition:
254+ $e
255+}
256+
257+```
258+
259+However, if a placeholder is assigned to a function, rules don't
260+implicitly filter out the zero values of placeholders that are used in
261+the match section.
262+The following example illustrates rules that filter out the zero values:
263+
264+```
265+rule ZeroValueFunctionPlaceholder {
266+ meta:
267+ events:
268+ // Even though $ph is used in the match section, there is no
269+ // implicit filtering of zero values for $ph, because $ph is assigned to a function.
270+ $ph = re.capture($e.principal.hostname, "some-regex")
271+
272+ match:
273+ $ph over 5m
274+
275+ condition:
276+ $e
277+}
278+
279+```
280+
281+To disable the implicit filtering of zero values,
282+you can use the `allow_zero_values` option in the [options section](#options_section_syntax).
283+
284+### Hop window
285+
286+By default, YARA-L 2.0 rules with a match section are evaluated using hop windows.
287+The time range of the rule's execution is divided into a set of overlapping hop windows,
288+each with the duration specified in the `match` section. Events are then correlated
289+within each hop window.
290+
291+For example, for a rule that is run over the time range [1:00, 2:00], with a
292+`match` section over `30m`, a possible set of overlapping hop windows
293+that could be generated is [1:00, 1:30], [1:03, 1:33] and [1:06, 1:36].
294+These windows are used to correlate multiple events.
295+
296+### Sliding window
297+
298+Using hop windows is not an effective way to search for events that happen in a specific order (for example, `e1` happens up to 2
299+minutes after `e2`). An occurrence of event `e1` and an occurrence of event `e2`
300+are correlated only if they fall into the same hop window generated.
301+
302+A more effective way to search for such event sequences is to use sliding windows.
303+Sliding windows with the duration specified in the `match` section are generated when
304+beginning or ending with a specified pivot event variable. Events are then
305+correlated within each sliding window. This makes it possible to search for
306+events that happen in a specific order (for example, `e1` happens within 2
307+minutes of `e2`). An occurrence of event `e1` and an occurrence of event `e2`
308+are correlated if event `e1` occurs within the sliding window duration after
309+event `e2`.
310+
311+Specify sliding windows in the `match` section of a rule as follows:
312+
313+`<match-var-1>, <match-var-2>, ... over <duration> before|after <pivot-event-var>`
314+
315+The pivot event variable is the event variable that sliding windows are based
316+on. If you use the `before` keyword, sliding windows are generated, ending with
317+each occurrence of the pivot event. If the `after` keyword is used, sliding
318+windows are generated beginning with each occurrence of the pivot event.
319+
320+The following are examples of valid sliding window usages:
321+
322+* `$var1, $var2 over 5m after $e1`
323+* `$user over 1h before $e2`
324+
325+See [a sliding window rule example](/chronicle/docs/detection/yara-l-2-0-overview#sliding_window_rule_example).
326+
327+**Note:** Using sliding windows instead of hop windows has been known to result in
328+slower performance. We recommend using sliding windows only for
329+specific cases, such as when event order is absolutely necessary or when
330+searching for the non-existence of events.
331+
332+We recommend not using sliding windows for single-event rules, because
333+sliding windows are designed to detect multiple events. If one of
334+your rules falls in this category, We recommend one of
335+the following workarounds:
336+
337+* Convert the rule to use multiple event variables, and update the condition
338+ section if the rule requires more than one occurrence of the event.
339+ + Optionally, consider adding timestamp filters instead of using a sliding window.
340+ For example, `$permission_change.metadata.event_timestamp.seconds < $file_creation.metadata.event_timestamp.seconds`
341+* Remove the sliding window.
342+
343+## Outcome section syntax
344+
345+In the `outcome` section, you can define up to 20 outcome variables, with
346+arbitrary names. These outcomes will be stored in the detections generated by
347+the rule. Each detection may have different values for the outcomes.
348+
349+The outcome name, `$risk_score`, is special. You can optionally define an
350+outcome with this name, and if you do, it must be an integer or float type. If populated,
351+the `risk_score` will be shown in the
352+[Enterprise Insights view](https://cloud.google.com/chronicle/docs/investigation/view-alerts-insights.md) for
353+alerts that come from rule detections.
354+
355+If you don't include a `$risk_score` variable in the outcome section of a rule,
356+one of the following default values is set:
357+
358+* If the rule is configured to generate an alert, then `$risk_score` is set to 40.
359+* If the rule is not configured to generate an alert, then `$risk_score` is set to 15.
360+
361+The value of `$risk_score` is stored in the `security_result.risk_score` UDM field.
362+
363+### Outcome variable data types
364+
365+Each outcome variable can have a different data type, which is determined by the expression
366+used to compute it. We support the following outcome data types:
367+
368+* integer
369+* floats
370+* string
371+* lists of integers
372+* lists of floats
373+* lists of strings
374+
375+### Conditional logic
376+
377+You can use conditional logic to compute the value of an outcome. Conditionals
378+are specified using the following syntax pattern:
379+
380+```
381+if(BOOL_CLAUSE, THEN_CLAUSE)
382+if(BOOL_CLAUSE, THEN_CLAUSE, ELSE_CLAUSE)
383+
384+```
385+
386+You can read a conditional expression as "if BOOL\_CLAUSE is true, then return
387+THEN\_CLAUSE, else return ELSE\_CLAUSE".
388+
389+BOOL\_CLAUSE must evaluate to a boolean value. A BOOL\_CLAUSE expression takes a
390+similar form as expressions in the `events` section. For example, it can
391+contain:
392+
393+* UDM field names with comparison operator, for example:
394+
395+ `if($context.graph.entity.user.title = "Vendor", 100, 0)`
396+* placeholder variable that was defined in the `events` section, for example:
397+
398+ `if($severity = "HIGH", 100, 0)`
399+* another outcome variable defined in the `outcome` section, for example:
400+
401+ `if($risk_score > 20, "HIGH", "LOW")`
402+* functions that return a boolean, for example:
403+
404+ `if(re.regex($e.network.email.from, `.*altostrat.com`), 100, 0)`
405+* look up in a [reference list](#reference_lists_syntax), for example:
406+
407+ `if($u.principal.hostname in %my_reference_list_name, 100, 0)`
408+* aggregation comparison, for example:
409+
410+ `if(count($login.metadata.event_timestamp.seconds) > 5, 100, 0)`
411+
412+The THEN\_CLAUSE and ELSE\_CLAUSE must be the same data type. We support integers, floats, and strings.
413+
414+You can omit the ELSE\_CLAUSE if the data type is integer or a float. If omitted, the
415+ELSE\_CLAUSE evaluates to 0. For example:
416+
417+```
418+`if($e.field = "a", 5)` is equivalent to `if($e.field = "a", 5, 0)`
419+
420+```
421+
422+You must provide the ELSE\_CLAUSE if the data type is string or if the THEN\_CLAUSE
423+is a placeholder variable or outcome variable.
424+
425+### Mathematical operations
426+
427+You can use mathematical operations to compute integer or float data type in the `outcome`and `events` sections of a rule. Google Security Operations supports addition, subtraction, multiplication, division, and modulus as top level operators in a computation.
428+
429+The following snippet is an example computation in the `outcome` section:
430+
431+```
432+outcome:
433+ $risk_score = max(100 + if($severity = "HIGH", 10, 5) - if($severity = "LOW", 20, 0))
434+
435+```
436+
437+Mathematical operations are allowed on the following types of operands as long as
438+each operand and the entire arithmetic expression is properly aggregated (See [Aggregations](#aggregations)):
439+
440+* Numeric event fields
441+* Numeric placeholder variables defined in the `events` section
442+* Numeric outcome variables defined in the `outcome` section
443+* Functions returning ints or floats
444+* Aggregations returning ints or floats
445+
446+Modulus is not allowed on floats.
447+
448+### Placeholder variables in outcomes
449+
450+When computing outcome variables, you can use placeholder variables which were
451+defined in the events section of your rule. In this example, assume that
452+`$email_sent_bytes` was defined in the events section of the rule:
453+
454+Single-event example:
455+
456+```
457+// No match section, so this is a single-event rule.
458+
459+outcome:
460+ // Use placeholder directly as an outcome value.
461+ $my_outcome = $email_sent_bytes
462+
463+ // Use placeholder in a conditional.
464+ $other_outcome = if($file_size > 1024, "SEVERE", "MODERATE")
465+
466+condition:
467+ $e
468+
469+```
470+
471+Multi-event example:
472+
473+```
474+match:
475+ // This is a multi event rule with a match section.
476+ $hostname over 5m
477+
478+outcome:
479+ // Use placeholder directly in an aggregation function.
480+ $max_email_size = max($email_sent_bytes)
481+
482+ // Use placeholder in a mathematical computation.
483+ $total_bytes_exfiltrated = sum(
484+ 1024
485+ + $email_sent_bytes
486+ + $file_event.principal.file.size
487+ )
488+
489+condition:
490+ $email_event and $file_event
491+
492+```
493+
494+### Outcome variables in outcome assignment expressions
495+
496+Outcome variables can be used to derive other outcome variables, similar to
497+placeholder variables defined in the `events` section. You can refer to an outcome
498+variable in the assignment of another outcome variable with a `$` token followed
499+by the variable name. Outcome variables must be defined before they can be referenced
500+in the rule text. When used in an assignment expression, outcome variables must
501+not be aggregated (See [Aggregations](#aggregations)).
502+
503+In the following example, the outcome variable `$risk_score` derives its
504+value from the outcome variable `$event_count`:
505+
506+Multi-event example:
507+
508+```
509+match:
510+ // This is a multi event rule with a match section.
511+ $hostname over 5m
512+
513+outcome:
514+ // Aggregates all timestamp on login events in the 5 minute match window.
515+ $event_count = count($login.metadata.event_timestamp.seconds)
516+
517+ // $event_count cannot be aggregated again.
518+ $risk_score = if($event_count > 5, "SEVERE", "MODERATE")
519+
520+ // This is the equivalent of the 2 outcomes above combined.
521+ $risk_score2 = if(count($login.metadata.event_timestamp.seconds) > 5, "SEVERE", "MODERATE")
522+
523+condition:
524+ $e
525+
526+```
527+
528+Outcome variables can be used in any type of expression on the right-hand-side of an outcome assignment,
529+except in the following expressions:
530+
531+* Aggregations
532+* `Arrays.length()` function calls
533+* With `any` or `all` modifiers
534+
535+### Aggregations
536+
537+Repeated event fields are non-scalar values. That is, a single variable points to
538+multiple values. For example, the event field variable `$e.target.ip` is a repeated field
539+and can have zero, one, or many ip values. It is a non-scalar value. Whereas the event field variable
540+`$e.principal.hostname` is not a repeated field and only has 1 value (i.e. a scalar value).
541+
542+Similarly, both non-repeated event fields and repeated event fields used in the outcome section
543+of a rule with a match window are non-scalar values. For example, the following rule groups events
544+using a match section and refers to a non-repeated event field in the outcome section:
545+
546+```
547+rule OutcomeAndMatchWindow{
548+ ...
549+ match:
550+ $userid over 5m
551+ outcome:
552+ $hostnames = array($e.principal.hostname)
553+ ...
554+}
555+
556+```
557+
558+Any 5-minute window the rule executes over might contain zero, one, or many events. The outcome section
559+operates on all events in a match window. Any event field variable referred to within the
560+outcome section can point to zero, one, or many values of the field on each event in the match window.
561+For example, if a 5-minute window contains 5 `$e` events, `$e.principal.hostname`
562+in the outcome section points to five different hostnames. The event field variable
563+`$e.principal.hostname` is treated as a non-scalar value in the `outcome` section of this rule.
564+
565+Because outcome variables must always yield a single scalar value, any non-scalar value which
566+an outcome assignment depends on must be aggregated to yield a single scalar value.
567+In an outcome section, the following are non-scalar values and must be aggregated:
568+
569+* Event fields (repeated or non-repeated) when the rule uses a match section
570+* Event placeholders (repeated or non-repeated) when the rule uses a match section
571+* Repeated event fields when the rule does not use a match section
572+* Repeated event placeholders when the rule does not use a match section
573+
574+Scalar event fields, scalar event placeholders, and constants can be wrapped in
575+aggregation functions in rules that don't include a match section. However, in
576+most cases, these aggregations return the wrapped value, making them unnecessary.
577+An exception is the `array()` aggregation, which you can use to explicitly convert
578+a scalar value into an array.
579+
580+Outcome variables are treated like aggregations: they must not be re-aggregated
581+when referred to in another outcome assignment.
582+
583+You can use the following aggregation functions:
584+
585+* `max()`: outputs the maximum over all possible values. Only works with integer and float.
586+* `min()`: outputs the minimum over all possible values. Only works with integer and float.
587+* `sum()`: outputs the sum over all possible values. Only works with integer and float.
588+* `count_distinct()`: collects all possible values, then outputs the distinct count of
589+ possible values.
590+* `count()`: behaves like `count_distinct()`, but returns a non-distinct count of
591+ possible values.
592+* `array_distinct()`: collects all possible distinct values, then outputs a list of these values. It
593+ will truncate the list of distinct values to 25 random elements. The deduplication
594+ to get a distinct list is applied first, then the truncation is applied.
595+* `array()`: behaves like `array_distinct()`, but returns a non-distinct list of
596+ values. It also truncates the list of values to 25 random elements.
597+* `period_start_for_max()`: start of the time period where the maximum of
598+ the listed value occurred.
599+* `period_start_for_min()`: start of the time period where the minimum of
600+ the listed value occurred.
601+
602+The aggregate function is important when a rule includes a `condition` section
603+that specifies multiple events must exist, because the aggregate function will
604+operate on all the events that generated the detection.
605+
606+For example, if your `outcome` and `condition` sections contain:
607+
608+```
609+outcome:
610+ $asset_id_count = count($event.principal.asset_id)
611+ $asset_id_distinct_count = count_distinct($event.principal.asset_id)
612+
613+ $asset_id_list = array($event.principal.asset_id)
614+ $asset_id_distinct_list = array_distinct($event.principal.asset_id)
615+
616+condition:
617+ #event > 1
618+
619+```
620+
621+Since the condition section requires there to be more than one `event` for each
622+detection, the aggregate functions will operate on multiple events. Suppose the
623+following events generated one detection:
624+
625+```
626+event:
627+ // UDM event 1
628+ asset_id="asset-a"
629+
630+event:
631+ // UDM event 2
632+ asset_id="asset-b"
633+
634+event:
635+ // UDM event 3
636+ asset_id="asset-b"
637+
638+```
639+
640+Then the values of your outcomes will be:
641+
642+* $asset\_id\_count = `3`
643+* $asset\_id\_distinct\_count = `2`
644+* $asset\_id\_list = `["asset-a", "asset-b", "asset-b"]`
645+* $asset\_id\_distinct\_list = `["asset-a", "asset-b"]`
646+
647+#### Things to know when using the outcome section:
648+
649+Other notes and restrictions:
650+
651+* The `outcome` section cannot reference a new placeholder variable which
652+ wasn't already defined in the `events` section or in the `outcome` section.
653+* The `outcome` section cannot use event variables that have not
654+ been defined in the `events` section.
655+* The `outcome` section can use an event field that was not
656+ used in the `events` section, given that the event variable that the event
657+ field belongs to was already defined in the `events` section.
658+* The `outcome` section can only correlate event variables that have already
659+ been correlated in the `events` section. Correlations happen when two
660+ event fields from different event variables are equated.
661+
662+You can find an example using the outcome section in
663+[Overview of the YARA-L 2.0](/chronicle/docs/detection/yara-l-2-0-overview#rule_with_outcome_section_example).
664+See [Create context-aware analytics](/chronicle/docs/detection/context-aware-analytics#outcome_section) for details on detection
665+deduping with the outcome section.
666+
667+## Condition section syntax
668+
669+* specify a match condition over events and placeholders defined in the `events` section. See the following section, *Event and placeholder conditionals*, for more details.
670+* (optional) use the `and` keyword to specify a match condition using outcome variables defined in the `outcome` section. See the following section, *Outcome conditionals*, for more details.
671+
672+### Count character
673+
674+The `#` character is a special character in the `condition` section. If it is
675+used before any event or placeholder variable name, it represents the number of
676+distinct events or values that satisfy all of the `events` section conditions.
677+
678+For example, `#c > 1` means the variable `c` must occur more than 1 time.
679+
680+### Value character
681+
682+The `$` character is a special character in the `condition` section. If it is
683+used before any outcome variable name, it represents the value of that outcome.
684+
685+If it is used before any event or placeholder variable name (for example,
686+`$event`), it represents `#event > 0`.
687+
688+### Event and placeholder conditionals
689+
690+List condition predicates for events and placeholder variables here, joined
691+with the keyword `and` or `or`. The keyword `and` can be used between any
692+conditions, but the keyword `or` can only be used when the rule only has a
693+single event variable.
694+
695+A valid example of using `or` between two placeholders on the same event:
696+
697+```
698+rule ValidConditionOr {
699+ meta:
700+ events:
701+ $e.metadata.event_type = "NETWORK_CONNECTION"
702+
703+ // Note that all placeholders use the same event variable.
704+ $ph = $e.principal.user.userid // Define a placeholder variable to put in match section.
705+ $ph2 = $e.principal.ip // Define a second placeholder variable to put in condition section.
706+ $ph3 = $e.principal.hostname // Define a third placeholder variable to put in condition section.
707+
708+ match:
709+ $ph over 5m
710+
711+ condition:
712+ $ph2 or $ph3
713+}
714+
715+```
716+
717+An invalid example of using `or` between two conditions on different events:
718+
719+```
720+rule InvalidConditionOr {
721+ meta:
722+ events:
723+ $e.metadata.event_type = "NETWORK_CONNECTION"
724+ $e2.graph.metadata.entity_type = "FILE"
725+ $e2.graph.entity.hostname = $e.principal.hostname
726+
727+ $ph = $e.principal.user.userid // Define a placeholder variable to put in match section.
728+
729+ match:
730+ $ph over 5m
731+
732+ condition:
733+ $e or $e2 // This line will cause an error because there is an or between events.
734+}
735+
736+```
737+
738+**Note:** Don't use the keyword `not` in event and placeholder conditionals.
739+
740+### Bounded and Unbounded conditions
741+
742+The following conditions are bounded conditions. They force the associated
743+event variable to exist, meaning that at least one occurrence of the event must
744+appear in any detection.
745+
746+* `$var // equivalent to #var > 0`
747+* `#var > n // where n >= 0`
748+* `#var >= m // where m > 0`
749+
750+The following conditions are unbounded conditions. They allow the associated
751+event variable to not exist, meaning that it is possible that no occurrence of
752+the event appears in a detection and any reference to fields on the event
753+variable will yield a zero value. Unbounded conditions can be used to detect
754+the absence of an event over a period of time. For example, a threat event
755+without a mitigation event within a 10 minute window. Rules using unbounded
756+conditions are called non-existence rules.
757+
758+* `!$var // equivalent to #var = 0`
759+* `#var >= 0`
760+* `#var < n // where n > 0`
761+* `#var <= m // where m >= 0`
762+
763+**Note:** For non-existence rules, the detection engine adds a 1 hour delay to the
764+expected latency (based on the rule's run frequency) to allow for late-arriving
765+data.
766+
767+#### Requirements for non-existence
768+
769+For a rule with non-existence to compile, it must satisfy the following requirements:
770+
771+1. At least one UDM event must have a bounded condition (that is, at least one UDM event must exist).
772+2. If a placeholder has an unbounded condition, it must be associated with
773+ at least one bounded UDM event.
774+3. If an entity has an unbounded condition, it must be associated with at
775+ least one bounded UDM event.
776+
777+Consider the following rule with the condition section omitted:
778+
779+```
780+rule NonexistenceExample {
781+ meta:
782+ events:
783+ $u1.metadata.event_type = "NETWORK_CONNECTION" // $u1 is a UDM event.
784+ $u2.metadata.event_type = "NETWORK_CONNECTION" // $u2 is a UDM event.
785+ $e1.graph.metadata.entity_type = "FILE" // $e1 is an Entity.
786+ $e2.graph.metadata.entity_type = "FILE" // $e2 is an Entity.
787+
788+ $user = $u1.principal.user.userid // Match variable is required for Multi-Event Rule.
789+
790+ // Placeholder Associations:
791+ // u1 u2
792+ // | \ /
793+ // port ip
794+ // | \
795+ // e1 e2
796+ $u1.target.port = $port
797+ $e1.graph.entity.port = $port
798+ $u1.principal.ip = $ip
799+ $u2.target.ip = $ip
800+ $e2.graph.entity.ip = $ip
801+
802+ // UDM-Entity Associations:
803+ // u1 - u2
804+ // | \ |
805+ // e1 e2
806+ $u1.metadata.event_type = $u2.metadata.event_type
807+ $e1.graph.entity.hostname = $u1.principal.hostname
808+ $e2.graph.entity.hostname = $u1.target.hostname
809+ $e2.graph.entity.hostname = $u2.principal.hostname
810+
811+ match:
812+ $user over 5m
813+
814+ condition:
815+ <condition_section>
816+}
817+
818+```
819+
820+The following are *valid* examples for the `<condition_section>`:
821+
822+* `$u1 and !$u2 and $e1 and $e2`
823+ + All UDM events and entities are present in the condition section.
824+ + At least one UDM event is bounded.
825+* `$u1 and !$u2 and $e1 and !$e2`
826+ + `$e2`is unbounded, which is allowed because it is associated with `$u1`, which is bounded. If `$e2` was not associated with `$u1`, this would be invalid.
827+* `#port > 50 and #ip = 0`
828+ + No UDM events and entities are present in the condition section; however, the placeholders that are present cover all the UDM events and entities.
829+ + `$ip` is assigned to both `$u1` and `$u2` and `#ip = 0` is an unbounded condition. However, bounded conditions are *stronger* than unbounded conditions. Since `$port` is assigned to `$u1` and `#port > 50` is a bounded condition, `$u1` is still bounded.
830+
831+The following are *invalid* examples for the `<condition_section>`:
832+
833+* `$u1 and $e1`
834+ + Every UDM event and entity appearing in the Events Section must appear in
835+ the Condition Section (or have a placeholder assigned to it that appears in the Condition Section).
836+* `$u1, $u2, $e1, $u2, #port > 50`
837+ + Commas are not allowed as condition separators.
838+* `!$u1 and !$u2 and $e1 and $e2`
839+ + Violates the first requirement that at least one UDM event is bounded.
840+* `($u1 or #port < 50) and $u2 and $e1 and $e2`
841+ + `or` keyword is not supported with unbounded conditions.
842+* `($u1 or $u2) and $e1 and $e2`
843+ + `or` keyword is not supported between different event variables.
844+* `not $u1 and $u2 and $e1 and $e2`
845+ + `not` keyword is not allowed for event and placeholder conditions.
846+* `#port < 50 and #ip = 0`
847+ + The placeholders that are present cover all the UDM events and entities; however, all of the conditions are unbounded. This means none of the UDM events are bounded, causing the rule to fail to compile.
848+
849+**Note:** Don't use a `match` variable in the `condition` section. It is a semantic
850+error since events are grouped by the `match` variable value.**Note:** Don't specify only **unbounded conditions** on all `event` variables that a `match` variable is assigned to. It is a semantic error. For a `match` variable value to be returned, at least one event must exist that contains the value.**Note:** In case of using a sliding window, the pivot event variable must be involved in at least one bounded condition.
851+
852+### Outcome conditionals
853+
854+List condition predicates for outcome variables here, joined with the keyword `and` or `or`, or preceded by the keyword `not`.
855+
856+Specify outcome conditionals differently depending on the type of the outcome variable:
857+
858+* **integer**: compare against an integer literal with operators `=, >, >=, <, <=, !=`, for example:
859+
860+ `$risk_score > 10`
861+* **float**: compare against a float literal with operators `=, >, >=, <, <=, !=`, for example:
862+
863+ `$risk_score <= 5.5`
864+* **string**: compare against a string literal with either `=` or `!=`, for example:
865+
866+ `$severity = "HIGH"`
867+* **list of integers or arrays**: specify condition using the `arrays.contains` function, for example:
868+
869+ `arrays.contains($event_ids, "id_1234")`
870+
871+**Note:** If you use the keyword `or` inside the <event/placeholder conditionals> subsection, you must surround that entire subsection with parentheses.
872+For example, the following is valid: `($e1 or $e2) and $outcome > 0`.
873+
874+#### Rule classification
875+
876+Specifying an outcome conditional *in a rule that has a match section* means that the rule will be classified as a **multi-event** rule for rule quota.
877+See [single event rule](/chronicle/docs/detection/yara-l-2-0-overview#single_event_rule) and [multiple event rule](/chronicle/docs/detection/yara-l-2-0-overview#multiple_event_rule) for more information about single and multiple event classifications.
878+
879+## Options section syntax
880+
881+In the `options` section, you can specify the options for the rule. Here is
882+an example of how to specify the options section:
883+
884+```
885+rule RuleOptionsExample {
886+ // Other rule sections
887+
888+ options:
889+ allow_zero_values = true
890+}
891+
892+```
893+
894+You can specify options using the syntax `key = value`, where `key` must be a
895+predefined option name and `value` must be a valid value for the option, as
896+specified for the following options:
897+
898+### allow\_zero\_values
899+
900+The valid values for this option are `true` and `false`, which determine
901+if this option is enabled or not. The default value is `false`. This option is
902+disabled if it is not specified in the rule.
903+
904+To enable this setting, add the following
905+to the options section of your rule: `allow_zero_values = true`. Doing so
906+will prevent the rule from implicitly filtering out the
907+zero values of placeholders that are used in the match section, as
908+described in [zero value handling in the match section](#zero_value_handling_in_the_match_section).
909+
910+### suppression\_window
911+
912+The `suppression_window` option lets you control how often a rule triggers a
913+detection. It prevents the same rule from generating multiple detections within
914+a specified time window, even if the rule's conditions are met multiple times.
915+Suppression windowing uses a tumbling window approach, which suppresses
916+duplicates over a fixed-size, non-overlapping window.
917+
918+You can optionally provide a `suppression_key` to further refine which instances
919+of the rule are suppressed within the suppression window. If not specified, all
920+instances of the rule are suppressed. This key is defined as an outcome variable.
921+
922+In the following example, `suppression_window` is set to `5m` and `suppression_key` is
923+set to the `$hostname` variable. After the rule triggers a detection for
924+`$hostname`, any further detections for `$hostname` are suppressed for the next
925+five minutes. However, if the rule triggers on an event with a different hostname,
926+a detection is created.
927+
928+The default value of `suppression_window` is `0`; that is, the suppression
929+window is disabled by default. This option only works for [single event rules](/chronicle/docs/detection/yara-l-2-0-overview#single-event-rule)
930+that don't have a `match` section.
931+
932+Example:
933+
934+```
935+rule SuppressionWindowExample {
936+ // Other rule sections
937+
938+ outcome:
939+ $suppression_key = $hostname
940+
941+ options:
942+ suppression_window = 5m
943+}
944+
945+```
946+
947+## Composite detection rules
948+
949+**Note:** This feature is covered by [Pre-GA Offerings Terms](https://chronicle.security/legal/service-terms/) of the Google Security Operations Service
950+Specific Terms. Pre-GA features might have limited support, and changes to pre-GA features might not be compatible with other pre-GA versions.
951+For more information, see the [Google SecOps Technical Support Service guidelines](https://chronicle.security/legal/technical-support-services-guidelines/)
952+and the [Google SecOps Service Specific Terms](https://chronicle.security/legal/service-terms/).
953+
954+Composite detection in Google SecOps involves connecting multiple
955+YARA-L rules. This sections explains how to build a
956+composite rule. For an overview of composite detections,
957+see [Overview of composite detections](/chronicle/docs/detection/composite-detections).
958+
959+### Rule structure
960+
961+Composite detection rules are always multi-event rules and follow the same
962+[structure and syntax](/chronicle/docs/detection/yara-l-2-0-syntax#rule_structure).
963+The following requirements apply to composite detection rules:
964+
965+* Composite rules must use a `match` section to define detection trigger conditions.
966+* Rules that use both detection fields and UDM events must explicitly join these
967+ data sources.
968+
969+For information on rule limitations, see [Limitations](/chronicle/docs/detection/composite-detections#limitations).
970+
971+### Use detections as input to rules
972+
973+Composite rules can reference rule detections generated by any custom or curated rule.
974+Google SecOps provides two methods for doing this.
975+
976+#### Reference detection content using outcome variables, match variables, or meta labels
977+
978+To access data from a detection without referencing the original UDM events,
979+use `outcome` variables, `match` variables, or `meta` labels. We recommend this
980+approach because it provides greater flexibility and better compatibility across
981+different rule types.
982+
983+For example, multiple rules can store a string (such as a URL, filename, or
984+registry key) in a common `outcome` variable if you're looking for that string
985+across different contexts. To access this string from a composite rule, start
986+with `detection` and locate the relevant information using elements from the
987+[Collection resource](/chronicle/docs/reference/rest/v1alpha/Collection).
988+
989+**Example:**
990+For example, suppose a detection rule produces the following information:
991+
992+* Outcome variable: `dest_domain = "cymbal.com"`
993+* UDM field: `target.hostname = "cymbal.com"`
994+
995+In the composite rule, you can access this data using the following paths:
996+
997+* `detection.detection.outcomes["dest_domain"]` to access the `dest_domain`
998+ outcome variable.
999+* `detection.collection_elements.references.event.target.hostname` to access
1000+ the `target.hostname` UDM field.
1001+* `detection.time_window.start_time.seconds` to access the detection timestamp.
1002+
1003+The Collection API and the `SecurityResult` API provide access to both:
1004+
1005+* Detection metadata and outcome values (`detection.detection`)
1006+* Underlying UDM events from referenced rules (`collection_elements`)
1007+
1008+#### Reference detection content using rule ID or rule name
1009+
1010+You can reference a rule by either its name or ID. We recommend this
1011+approach when your detection logic depends on specific rules. Referencing
1012+relevant rules by name or ID improves performance and prevents timeouts by
1013+reducing the data analyzed. For example, you can directly query fields like
1014+`target.url` or `principal.ip` from a known previous detection.
1015+
1016+* **Reference a rule by rule ID (recommended):** use the
1017+ `detection.detection.rule_id` field to reference a rule by ID. You can find the
1018+ rule ID in the rule's URL in Google SecOps. User-generated rules
1019+ have IDs in the format `ru_UUID`, while curated detections have IDs in the
1020+ format `ur_UUID`. For example:
1021+
1022+ `detection.detection.rule_id = "ru_e0d3f371-6832-4d20-b0ad-1f4e234acb2b"`
1023+* **Reference a rule by a rule name:** use the `detection.detection.rule_name`
1024+ field to reference a rule by name. You can specify the exact rule name or use a
1025+ regular expression to match it. For example:
1026+
1027+ + `detection.detection.rule_name = "My Rule Name"`
1028+ + `detection.detection.rule_name = "/PartOfName/"`
1029+
1030+**Note:** We recommend using rule IDs for referencing because IDs are unique and
1031+don't change. Rule names can be modified, which could potentially break your
1032+composite detection.
1033+
1034+### Combine events and detections
1035+
1036+Composite rules can combine different data sources, including UDM events, entity
1037+graph data, and detection fields. The following guidelines apply:
1038+
1039+* **Use distinct variables per source**—Assign unique event variables to each data source (for example, `$e` for
1040+ events, `$d` for detections), where the data source includes events, entities,
1041+ and detections.
1042+* **Join sources on shared context**—Connect data sources using common values, such as user IDs, IP addresses, or
1043+ domain names in your rule's conditions.
1044+* **Define a match window**—Always include a `match` clause with a time window no longer than 48 hours.
1045+
1046+For example:
1047+
1048+```
1049+rule CheckCuratedDetection_with_EDR_and_EG {
1050+ meta:
1051+ author = "noone@cymbal.com"
1052+ events:
1053+ $d.detection.detection.rule_name = /SCC: Custom Modules: Configurable Bad Domain/
1054+ $d.detection.collection_elements.references.event.network.dns.questions.name = $domain
1055+ $d.detection.collection_elements.references.event.principal.asset.hostname = $hostname
1056+
1057+ $e.metadata.log_type = "LIMACHARLIE_EDR"
1058+ $e.metadata.product_event_type = "NETWORK_CONNECTIONS"
1059+ $domain = re.capture($e.principal.process.command_line, "\\s([a-zA-Z0-9.-]+\\.[a-zA-Z0-9.-]+)$")
1060+ $hostname = re.capture($e.principal.hostname, "([^.]*)")
1061+
1062+ $prevalence.graph.metadata.entity_type = "DOMAIN_NAME"
1063+ $prevalence.graph.metadata.source_type = "DERIVED_CONTEXT"
1064+ $prevalence.graph.entity.hostname = $domain
1065+ $prevalence.graph.entity.domain.prevalence.day_count = 10
1066+ $prevalence.graph.entity.domain.prevalence.rolling_max <= 5
1067+ $prevalence.graph.entity.domain.prevalence.rolling_max > 0
1068+
1069+ match:
1070+ $hostname over 1h
1071+
1072+ outcome:
1073+ $risk_score = 80
1074+ $CL_target = array($domain)
1075+
1076+ condition:
1077+ $e and $d and $prevalence
1078+}
1079+
1080+```
1081+
1082+### Create sequential composite detections
1083+
1084+Sequential composite detections identify patterns of related events where the
1085+sequence of detections is important, such as a brute-force login attempt
1086+detection, followed by a successful login. These patterns can combine multiple
1087+base detections, raw UDM events, or both.
1088+
1089+To create a sequential composite detection, you must enforce that order within
1090+your rule. To enforce the expected sequence, use one of the following methods:
1091+
1092+* **Sliding windows:** Define the sequence of detections using sliding windows
1093+ in your `match` conditions.
1094+* **Timestamp comparisons:** Compare the timestamps of detections within your
1095+ rule logic to ensure that they happen in the selected order.
1096+
1097+For example:
1098+
1099+```
1100+events:
1101+ $d1.detection.detection.rule_name = "fileEvent_rule"
1102+ $userid = $d1.detection.detection.outcomes["user"]
1103+ $hostname = $d1.detection.detection.outcomes["hostname"]
1104+
1105+ $d2.detection.detection.rule_name = "processExecution_rule"
1106+ $userid = $d2.detection.detection.outcomes["user"]
1107+ $hostname = $d2.detection.detection.outcomes["hostname"]
1108+
1109+ $d3.detection.detection.rule_name = "networkEvent_rule"
1110+ $userid = $d3.detection.detection.outcomes["user"]
1111+ $hostname = $d3.detection.detection.outcomes["hostname"]
1112+
1113+$d3.detection.collection_elements.references.event.metadata.event_timestamp.seconds > $d2.detection.collection_elements.references.event.metadata.event_timestamp.seconds
1114+
1115+ match:
1116+ $userid over 24h after $d1
1117+
1118+```
1119+
1120+## Boolean expressions
1121+
1122+Boolean expressions are expressions with a boolean type.
1123+
1124+### Comparisons
1125+
1126+For a binary expression to use as condition, use the following syntax:
1127+
1128+* `<EXPR> <OP> <EXPR>`
1129+
1130+Expression can be either event field, variable, literal, or function expression.
1131+
1132+For example:
1133+
1134+* `$e.source.hostname = "host1234"`
1135+* `$e.source.port < 1024`
1136+* `1024 < $e.source.port`
1137+* `$e1.source.hostname != $e2.target.hostname`
1138+* `$e1.metadata.collected_timestamp.seconds > $e2.metadata.collected_timestamp.seconds`
1139+* `$port >= 25`
1140+* `$host = $e2.target.hostname`
1141+* `"google-test" = strings.concat($e.principal.hostname, "-test")`
1142+* `"email@google.org" = re.replace($e.network.email.from, "com", "org")`
1143+
1144+If both sides are literals, it is regarded as a compilation error.
1145+
1146+### Functions
1147+
1148+Some function expressions return boolean value, which can be used as an individual predicate in the `events` section. Such functions are:
1149+
1150+* `re.regex()`
1151+* `net.ip_in_range_cidr()`
1152+
1153+For example:
1154+
1155+* `re.regex($e.principal.hostname, `.*\.google\.com`)`
1156+* `net.ip_in_range_cidr($e.principal.ip, "192.0.2.0/24")`
1157+
1158+### Reference list expressions
1159+
1160+You can use reference lists in the events section. See the section on
1161+[Reference Lists](#reference_lists_syntax) for more details.
1162+
1163+### Logical expressions
1164+
1165+You can use the logical `and` and logical `or` operators in the `events` section as shown in the following examples:
1166+
1167+* `$e.metadata.event_type = "NETWORK_DNS" or $e.metadata.event_type = "NETWORK_DHCP"`
1168+* `($e.metadata.event_type = "NETWORK_DNS" and $e.principal.ip = "192.0.2.12") or ($e.metadata.event_type = "NETWORK_DHCP" and $e.principal.mac = "AB:CD:01:10:EF:22")`
1169+* `not $e.metadata.event_type = "NETWORK_DNS"`
1170+
1171+By default, the precedence order from highest to lowest is `not`, `and`, `or`.
1172+
1173+For example, "a or b and c" is evaluated as "a or (b and c)" when the operators `or` and `and` are defined explicitly in the expression.
1174+
1175+In the `events` section, predicates are joined using the `and` operator if an operator is not explicitly defined.
1176+
1177+The order of evaluation may be different if the `and` operator is implied in the expression.
1178+
1179+For example, consider the following comparison expressions where `or` is defined explicitly. The `and` operator is implied.
1180+
1181+```
1182+$e1.field = "bat"
1183+or $e1.field = "baz"
1184+$e2.field = "bar"
1185+
1186+```
1187+
1188+This example is interpreted as follows:
1189+
1190+```
1191+($e1.field = "bat" or $e1.field = "baz")
1192+and ($e2.field = "bar")
1193+
1194+```
1195+
1196+Because `or` is defined explicitly, the predicates surrounding `or` are grouped and evaluated first.
1197+The last predicate, `$e2.field = "bar"` is joined implicitly using `and`. The result is that order of evaluation changes.
1198+
1199+**Note:** There is a limit on the number of `and` and `or` values you can specify for a
1200+single rule. This limit varies depending on the complexity of the rule and the
1201+complexity of the data in your Google SecOps account. Contact your Google SecOps representative for information on alternatives to this type of
1202+rule.
1203+
1204+## Enumerated types
1205+
1206+You can use the operators with [enumerated](/chronicle/docs/reference/udm-field-list#event_enumerated_types) types. It can be applied to rules to simplify and optimize (use operator instead of reference lists) the performance.
1207+
1208+In the following example, 'USER\_UNCATEGORIZED' and 'USER\_RESOURCE\_DELETION' correspond to 15000 and 15014, so the rule will look for all the listed events:
1209+
1210+```
1211+$e.metadata.event_type >= "USER_CATEGORIZED" and $e.metadata.event_type <= "USER_RESOURCE_DELETION"
1212+
1213+```
1214+
1215+List of events:
1216+
1217+* USER\_RESOURCE\_DELETION
1218+* USER\_RESOURCE\_UPDATE\_CONTENT
1219+* USER\_RESOURCE\_UPDATE\_PERMISSIONS
1220+* USER\_STATS
1221+* USER\_UNCATEGORIZED
1222+
1223+## Nocase Modifier
1224+
1225+When you have a comparison expression between string values or a regular expression, you can append nocase at the end of the expression to ignore capitalization.
1226+
1227+* `$e.principal.hostname != "http-server" nocase`
1228+* `$e1.principal.hostname = $e2.target.hostname nocase`
1229+* `$e.principal.hostname = /dns-server-[0-9]+/ nocase`
1230+* `re.regex($e.target.hostname, `client-[0-9]+`) nocase`
1231+
1232+This cannot be used when a type of field is an enumerated value. The following
1233+examples are invalid and will generate compilation errors:
1234+
1235+* `$e.metadata.event_type = "NETWORK_DNS" nocase`
1236+* `$e.network.ip_protocol = "TCP" nocase`
1237+
1238+## Repeated fields
1239+
1240+In the Unified Data Model (UDM), some fields are labeled as repeated, which indicates
1241+that they are lists of values or other types of messages.
1242+
1243+### Repeated fields and boolean expressions
1244+
1245+There are 2 kinds of boolean expressions that act on repeated fields:
1246+
1247+1. Modified
1248+2. Unmodified
1249+
1250+Consider the following event:
1251+
1252+```
1253+event_original {
1254+ principal {
1255+ // ip is a repeated field
1256+ ip: [ "192.0.2.1", "192.0.2.2", "192.0.2.3" ]
1257+
1258+ hostname: "host"
1259+ }
1260+}
1261+
1262+```
1263+
1264+#### Modified expressions
1265+
1266+The following sections describe the purpose and how to use the `any` and `all` modifiers in expressions.
1267+
1268+##### any
1269+
1270+If *any* element of the repeated field satisfies the condition, the event as a whole satisfies the condition.
1271+
1272+* `event_original` satisfies `any $e.principal.ip = "192.0.2.1"`.
1273+* `event_original` fails `any $e.repeated_field.field_a = "9.9.9.9`.
1274+
1275+##### all
1276+
1277+If *all* elements of the repeated field satisfy the condition, the event as a whole satisfies the condition.
1278+
1279+* `event_original` satisfies `net.ip_in_range_cidr(all $e.principal.ip, "192.0.2.0/8")`.
1280+* `event_original` fails `all $e.principal.ip = "192.0.2.2"`.
1281+
1282+**Note:** To use `any` or `all` with a function, the modifier must precede the repeated field and not the function. For example, `re.regex(any $e.about.hostname, `server-[0-9]+`)` is valid while `any re.regex($e.about.hostname, `server-[0-9]+`)` is not.
1283+
1284+When writing a condition with `any` or `all`, be aware that negating the condition
1285+with `not` might not have the same meaning as using the negated operator.
1286+
1287+For example:
1288+
1289+* `not all $e.principal.ip = "192.168.12.16"` checks if not all IP addresses
1290+ match `192.168.12.16`, meaning the rule is checking whether at least one IP address
1291+ does not match `192.168.12.16`.
1292+* `all $e.principal.ip != "192.168.12.16"` checks if all IP addresses don't match
1293+ `192.168.12.16`, meaning the rule is checking that no IP addresses match to `192.168.12.16`.
1294+
1295+Constraints:
1296+
1297+* `any` and `all` operators are only compatible with repeated fields (not scalar fields).
1298+* `any` and `all` cannot be used to join two repeated fields. For example, `any $e1.principal.ip = $e2.principal.ip` is not valid.
1299+* `any` and `all` operators are not supported with the reference list expression.
1300+
1301+#### Unmodified expressions
1302+
1303+With unmodified expressions, each element in the repeated field is treated individually. If an event's repeated field contains *n* elements, then the rule is applied on *n* copies of the event, where each copy has one of the elements of the repeated field. These copies are transient and not stored.
1304+
1305+The rule is applied on the following copies:
1306+
1307+| event copy | principal.ip | principal.hostname |
1308+| --- | --- | --- |
1309+| event\_copy\_1 | "192.0.2.1" | "host" |
1310+| event\_copy\_2 | "192.0.2.2" | "host" |
1311+| event\_copy\_3 | "192.0.2.3" | "host" |
1312+
1313+If *any* event copy satisfies *all* unmodified conditions on the repeated field, the event as a whole satisfies all the conditions. That means that if you have multiple conditions on a repeated field, then the event copy must satisfy *all* of them. The following rule examples use the preceding example dataset to demonstrate this behavior.
1314+
1315+The following rule returns one match when run against the `event_original` example
1316+dataset, because `event_copy_1` satisfies all of the events predicates:
1317+
1318+```
1319+rule repeated_field_1 {
1320+ meta:
1321+ events:
1322+ net.ip_in_range_cidr($e.principal.ip, "192.0.2.0/8") // Checks if IP address matches 192.x.x.x
1323+ $e.principal.ip = "192.0.2.1"
1324+ condition:
1325+ $e
1326+}
1327+
1328+```
1329+
1330+The following rule doesn't return a match when run against the `event_original`
1331+example dataset, because there is no event copy in `$e.principal.ip` that
1332+satisfies *all* the event predicates.
1333+
1334+```
1335+rule repeated_field_2 {
1336+ meta:
1337+ events:
1338+ $e.principal.ip = "192.0.2.1"
1339+ $e.principal.ip = "192.0.2.2"
1340+ condition:
1341+ $e
1342+}
1343+
1344+```
1345+
1346+Modified expressions on repeated fields are compatible with unmodified expressions on repeated fields because the element list is the same for each event copy. Consider the following rule:
1347+
1348+```
1349+rule repeated_field_3 {
1350+ meta:
1351+ events:
1352+ any $e.principal.ip = "192.0.2.1"
1353+ $e.principal.ip = "192.0.2.3"
1354+ condition:
1355+ $e
1356+}
1357+
1358+```
1359+
1360+The rule is applied on the following copies:
1361+
1362+| event copy | principal.ip | any $e.principal.ip |
1363+| --- | --- | --- |
1364+| event\_copy\_1 | "192.0.2.1" | ["192.0.2.1", "192.0.2.2", "192.0.2.3"] |
1365+| event\_copy\_2 | "192.0.2.2" | ["192.0.2.1", "192.0.2.2", "192.0.2.3"] |
1366+| event\_copy\_3 | "192.0.2.3" | ["192.0.2.1", "192.0.2.2", "192.0.2.3"] |
1367+
1368+In this case, all copies satisfy `any $e.principal.ip = "192.0.2.1"` but only `event_copy_3` satisfies $e.principal.ip = "192.0.2.3". As a result, the event as a whole would match.
1369+
1370+Another way to think about these expression types are:
1371+
1372+* Expressions on repeated fields which use `any` or `all` operate on the list in `event_original`.
1373+* Expressions on repeated fields which don't use `any` or `all` operate on individual `event_copy_n` events.
1374+
1375+### Repeated fields and placeholders
1376+
1377+Repeated fields work with placeholder assignments. Similar to unmodified expressions on repeated fields, a copy of the event is made for each element. Using the same example of `event_copy`, the placeholder takes the value of the `event_copy_n`'s repeated field value, for each of the event copies where *n* is the event copy number. If the placeholder is used in the match section, this can result in multiple matches.
1378+
1379+The following example generates one match. The `$ip` placeholder is equal
1380+to `192.0.2.1` for `event_copy_1`, which satisfies the predicates in the rule.
1381+The match's event samples contain a single element, `event_original`.
1382+
1383+```
1384+// Generates 1 match.
1385+rule repeated_field_placeholder1 {
1386+ meta:
1387+ events:
1388+ $ip = $e.principal.ip
1389+ $ip = "192.0.2.1"
1390+ $host = $e.principal.hostname
1391+
1392+ match:
1393+ $host over 5m
1394+
1395+ condition:
1396+ $e
1397+}
1398+
1399+```
1400+
1401+The following example generates three matches. The `$ip` placeholder is equal
1402+to different values, for each of the different `event_copy_n` copies.
1403+The grouping is done on `$ip` since it is in the match section. Therefore, you get three matches
1404+where each match has a different value for the `$ip` match variable. Each match has the same
1405+event sample: a single element, `event_original`.
1406+
1407+```
1408+// Generates 3 matches.
1409+rule repeated_field_placeholder2 {
1410+ meta:
1411+ events:
1412+ $ip = $e.principal.ip
1413+ net.ip_in_range_cidr($ip, "192.0.2.0/8") // Checks if IP matches 192.x.x.x
1414+
1415+ match:
1416+ $ip over 5m
1417+
1418+ condition:
1419+ $e
1420+}
1421+
1422+```
1423+
1424+**Note:** `any` and `all` cannot be used when assigning a repeated field to a placeholder variable or joining with a field of another event. For example, `any $e.principal.ip = $ip` is not valid.
1425+
1426+#### Outcomes using placeholders assigned to repeated fields
1427+
1428+Placeholders are assigned to each *element* of each repeated field - not the entire list. Thus, when they're used in the outcome section, the outcome is calculated using only the elements that satisfied earlier sections.
1429+
1430+Consider the following rule:
1431+
1432+```
1433+rule outcome_repeated_field_placeholder {
1434+ meta:
1435+ events:
1436+ $ip = $e.principal.ip
1437+ $ip = "192.0.2.1" or $ip = "192.0.2.2"
1438+ $host = $e.principal.hostname
1439+
1440+ match:
1441+ $host over 5m
1442+
1443+ outcome:
1444+ $o = array_distinct($ip)
1445+
1446+ condition:
1447+ $e
1448+}
1449+
1450+```
1451+
1452+There are 4 stages of execution for this rule. The first stage is event copying:
1453+
1454+| event copy | $ip | $host | $e |
1455+| --- | --- | --- | --- |
1456+| event\_copy\_1 | "192.0.2.1" | "host" | event\_id |
1457+| event\_copy\_2 | "192.0.2.2" | "host" | event\_id |
1458+| event\_copy\_3 | "192.0.2.3" | "host" | event\_id |
1459+
1460+The events section will then filter out rows that don't match the filters:
1461+
1462+| event copy | $ip | $host | $e |
1463+| --- | --- | --- | --- |
1464+| event\_copy\_1 | "192.0.2.1" | "host" | event\_id |
1465+| event\_copy\_2 | "192.0.2.2" | "host" | event\_id |
1466+
1467+`event_copy_3` is filtered out because `"192.0.2.3"` does not satisfy `$ip = "192.0.2.1" or $ip = "192.0.2.2"`.
1468+
1469+The match section will then group by match variables and the outcome section will perform aggregation on each group:
1470+
1471+| $host | $o | $e |
1472+| --- | --- | --- |
1473+| "host" | ["192.0.2.1", "192.0.2.2"] | event\_id |
1474+
1475+`$o = array_distinct($ip)` is calculated using `$ip` from the previous stage and not the event copying stage.
1476+
1477+Finally, the condition section will filter each group. Since this rule just checks for the existence of $e, the row from earlier will produce a single detection.
1478+
1479+`$o` does not contain all the elements from `$e.principal.ip` because not all the elements satisfied all the conditions in the events section. However, all the elements of `e.principal.ip` will appear in the event sample because the event sample uses `event_original`.
1480+
1481+### Array indexing
1482+
1483+You can perform array indexing on repeated fields. To access the n-th repeated field element, use the standard list syntax (elements are 0-indexed). An out-of-bounds element returns the default value.
1484+
1485+* `$e.principal.ip[0] = "192.168.12.16"`
1486+* `$e.principal.ip[999] = ""` If there are fewer than 1000 elements, this evaluates to `true`.
1487+
1488+Constraints:
1489+
1490+* An index must be a non-negative integer literal. For example, `$e.principal.ip[-1]` is not valid.
1491+* Values that have an `int` type (for example, a placeholder set to `int`) don't count.
1492+* Array indexing cannot be combined with `any` or `all`. For example, `any $e.intermediary.ip[0]` is not valid.
1493+* Array indexing cannot be combined with map syntax. For example, `$e.additional.fields[0]["key"]` is not valid.
1494+* If the field path contains multiple repeated fields, all repeated fields must use array indexing. For example, `$e.intermediary.ip[0]` is not valid because `intermediary` and `ip` are both repeated fields, but there is only an index for `ip`.
1495+
1496+### Repeated messages
1497+
1498+When a [`message`](https://protobuf.dev/overview/#syntax) field is repeated, an unintended effect is to reduce the likelihood of a match. This is illustrated in the following examples.
1499+
1500+Consider the following event:
1501+
1502+```
1503+event_repeated_message {
1504+ // about is a repeated message field.
1505+ about {
1506+ // ip is a repeated string field.
1507+ ip: [ "192.0.2.1", "192.0.2.2", "192.0.2.3" ]
1508+
1509+ hostname: "alice"
1510+ }
1511+ about {
1512+ hostname: "bob"
1513+ }
1514+}
1515+
1516+```
1517+
1518+As stated for unmodified expressions on repeated fields, a temporary copy of the event is made for each element of the repeated field. Consider the following rule:
1519+
1520+```
1521+rule repeated_message_1 {
1522+ meta:
1523+ events:
1524+ $e.about.ip = "192.0.2.1"
1525+ $e.about.hostname = "bob"
1526+ condition:
1527+ $e
1528+}
1529+
1530+```
1531+
1532+The rule is applied on the following copies:
1533+
1534+| event copy | about.ip | about.hostname |
1535+| --- | --- | --- |
1536+| event\_copy\_1 | "192.0.2.1" | "alice" |
1537+| event\_copy\_2 | "192.0.2.2" | "alice" |
1538+| event\_copy\_3 | "192.0.2.3" | "alice" |
1539+| event\_copy\_4 | "" | "bob" |
1540+
1541+The event does not match on the rule because there exists no event copy that satisfies all of the expressions.
1542+
1543+#### Repeated messages and array indexing
1544+
1545+Another unexpected behavior can occur when using array indexing with unmodified expressions on repeated message fields. Consider the following example rule which uses array indexing:
1546+
1547+```
1548+rule repeated_message_2 {
1549+ meta:
1550+ events:
1551+ $e.about.ip = "192.0.2.1"
1552+ $e.about[1].hostname = "bob"
1553+ condition:
1554+ $e
1555+}
1556+
1557+```
1558+
1559+The rule is applied to the following copies:
1560+
1561+| event copy | about.ip | about[1].hostname |
1562+| --- | --- | --- |
1563+| event\_copy\_1 | "192.0.2.1" | "bob" |
1564+| event\_copy\_2 | "192.0.2.2" | "bob" |
1565+| event\_copy\_3 | "192.0.2.3" | "bob" |
1566+| event\_copy\_4 | "" | "bob" |
1567+
1568+Since `event_copy_1` satisfies all of the expressions in `repeated_message_2`, the event matches on the rule.
1569+
1570+This can lead to unexpected behavior because rule `repeated_message_1` lacked array indexing and produced no matches while rule `repeated_message_2` used array indexing and produced a match.
1571+
1572+## Comments
1573+
1574+Designate comments with two slash characters (`// comment`) or multi-line comments set off using slash asterisk characters (`/* comment */`), as you would in C.
1575+
1576+## Literals
1577+
1578+Nonnegative integers and floats, string, boolean, and regular expression literals are supported.
1579+
1580+### String and regular expression literals
1581+
1582+You can use either of the following quotation characters to enclose strings in YARA-L 2.0. However, quoted text is interpreted differently depending on which one you use.
1583+
1584+1. Double quotes (") — Use for normal strings. Must include escape characters.
1585+ For example: "hello\tworld" —\t is interpreted as a tab
1586+2. Back quotes (`) — Use to interpret all characters literally.
1587+ For example: `hello\tworld` —\t is not interpreted as a tab
1588+
1589+For regular expressions, you have two options.
1590+
1591+If you want to use regular expressions directly without the `re.regex()` function, use `/regex/` for the regular expression literals.
1592+
1593+You can also use string literals as regular expression literals when you use the `re.regex()` function. Note that for double quote string literals, you must escape backslash characters with backslash characters, which can look awkward.
1594+
1595+For example, the following regular expressions are equivalent:
1596+
1597+* `re.regex($e.network.email.from, `.*altostrat\.com`)`
1598+* `re.regex($e.network.email.from, ".*altostrat\\.com")`
1599+* `$e.network.email.from = /.*altostrat\.com/`
1600+
1601+Google recommends using back quote characters for strings in regular expressions for ease of readability.
1602+
1603+## Operators
1604+
1605+You can use the following operators in YARA-L:
1606+
1607+| | |
1608+| --- | --- |
1609+| **Operator** | **Description** |
1610+| = | equal/declaration |
1611+| != | not equal |
1612+| < | less than |
1613+| <= | less than or equal |
1614+| > | greater than |
1615+| >= | greater than or equal |
1616+
1617+## Variables
1618+
1619+In YARA-L 2.0, all variables are represented as `$<variable name>`.
1620+
1621+You can define the following types of variables:
1622+
1623+* Event variables — Represent groups of events in normalized form (UDM) or entity events. Specify conditions for event variables in the `events` section. You identify event variables using a name, event source, and event fields. Allowed sources are `udm` (for normalized events) and `graph` (for entity events). If the source is omitted, `udm` is set as the default source. Event fields are represented as a chain of *.<field name>* (for example, *$e.field1.field2*). Event field chains always start from the top-level source (UDM or Entity).
1624+* Match variables — Declare in the `match` section. Match variables become grouping fields for the query, as one row is returned for each unique set of match variables (and for each time window). When the rule finds a match, the match variable values are returned. Specify what each match variable represents in the `events` section.
1625+* Placeholder variables — Declare and define in the `events` section. Placeholder variables are similar to match variables. However, you can use placeholder variables in the `condition` section to specify match conditions.
1626+
1627+**Note:** Every placeholder variable **must** be mapped to an event field. For example, if you only referenced the following placeholder in this single line in a rule, it would fail to compile since *$var* is not bound to an event variable: *$e.field != $var*
1628+
1629+Use match variables and placeholder variables to declare relationships between event fields through transitive join conditions (see [Events Section Syntax](#events_section_syntax) for more detail).
1630+
1631+## Keywords
1632+
1633+Keywords in YARA-L 2.0 are case-insensitive. For example, `and` or `AND` are
1634+equivalent. Variable names must not conflict with keywords. For example,
1635+`$AND` or `$outcome` is invalid.
1636+
1637+The following are keywords for detection engine rules: `rule`, `meta`, `match`, `over`, `events`, `condition`, `outcome`, `options`, `and`, `or`, `not`, `nocase`, `in`, `regex`, `cidr`, `before`, `after`, `all`, `any`, `if`, `max`, `min`, `sum`, `array`, `array_distinct`, `count`, `count_distinct`, `is`, and `null`.
1638+
1639+### Maps
1640+
1641+YARA-L supports map access for Structs and Labels.
1642+
1643+#### Structs and Labels
1644+
1645+Some UDM fields use either the [Struct](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#struct) or [Label](/chronicle/docs/reference/udm-field-list#label) data type.
1646+
1647+To search for a specific key-value pair in both Struct and Label, use the standard map syntax:
1648+
1649+```
1650+// A Struct field.
1651+$e.udm.additional.fields["pod_name"] = "kube-scheduler"
1652+// A Label field.
1653+$e.metadata.ingestion_labels["MetadataKeyDeletion"] = "startup-script"
1654+
1655+```
1656+
1657+The map access always returns a string.
1658+
1659+#### Supported cases
1660+
1661+##### Events and Outcome Section
1662+
1663+```
1664+// Using a Struct field in the events section
1665+events:
1666+ $e.udm.additional.fields["pod_name"] = "kube-scheduler"
1667+
1668+// Using a Label field in the outcome section
1669+outcome:
1670+ $value = array_distinct($e.metadata.ingestion_labels["MetadataKeyDeletion"])
1671+
1672+```
1673+
1674+##### Assigning a map value to a Placeholder
1675+
1676+```
1677+$placeholder = $u1.metadata.ingestion_labels["MetadataKeyDeletion"]
1678+
1679+```
1680+
1681+##### Using a map field in a join condition
1682+
1683+```
1684+// using a Struct field in a join condition between two udm events $u1 and $u2
1685+$u1.metadata.event_type = $u2.udm.additional.fields["pod_name"]
1686+
1687+```
1688+
1689+#### Unsupported cases
1690+
1691+Maps are not supported in the following cases.
1692+
1693+##### Combining `any` or `all` keywords with a map
1694+
1695+For example, the following is not supported:
1696+
1697+```
1698+all $e.udm.additional.fields["pod_name"] = "kube-scheduler"
1699+
1700+```
1701+
1702+##### Other types of values
1703+
1704+The map syntax can only return a string value. In the case of
1705+[Struct](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#struct)
1706+data types, the map syntax can only access keys whose values are strings.
1707+Accessing keys whose values are other primitive types like integers, is not possible.
1708+
1709+#### Duplicate value handling
1710+
1711+Map accesses always returns a single value. In the uncommon
1712+edge case that the map access could refer to multiple values, the map
1713+access will deterministically return the first value.
1714+
1715+This can happen in either of the following cases:
1716+
1717+* A label has a duplicate key.
1718+
1719+ The label structure represents a map, but does not enforce key uniqueness.
1720+ By convention, a map should have unique keys, so Google SecOps does
1721+ not recommend populating a label with duplicate keys.
1722+
1723+ The rule text `$e.metadata.ingestion_labels["dupe-key"]` would return
1724+ the first possible value, `val1`, if run over the following data example:
1725+
1726+ ```
1727+ // Disrecommended usage of label with a duplicate key:
1728+ event {
1729+ metadata{
1730+ ingestion_labels{
1731+ key: "dupe-key"
1732+ value: "val1" // This is the first possible value for "dupe-key"
1733+ }
1734+ ingestion_labels{
1735+ key: "dupe-key"
1736+ value: "val2"
1737+ }
1738+ }
1739+ }
1740+
1741+ ```
1742+* A label has an ancestor repeated field.
1743+
1744+ A repeated field might contain a label as a child field. Two different
1745+ entries in the top-level repeated field might contain labels that
1746+ have the same key. The rule text `$e.security_result.rule_labels["key"]`
1747+ would return the first possible value, `val3`, if run over the following
1748+ data example:
1749+
1750+ ```
1751+ event {
1752+ // security_result is a repeated field.
1753+ security_result {
1754+ threat_name: "threat1"
1755+ rule_labels {
1756+ key: "key"
1757+ value: "val3" // This is the first possible value for "key"
1758+ }
1759+ }
1760+ security_result {
1761+ threat_name: "threat2"
1762+ rule_labels {
1763+ key: "key"
1764+ value: "val4"
1765+ }
1766+ }
1767+ }
1768+
1769+ ```
1770+
1771+## Functions
1772+
1773+This section describes the YARA-L 2.0 functions that you can use in detection
1774+engine rules and search.
1775+
1776+**Note:** The use of the event variable `$e` is optional when YARA-L is used in
1777+search. Both `principal.hostname` and `$e.principal.hostname` are supported in
1778+search.
1779+
1780+These functions can be used in the following parts of a YARA-L rule:
1781+
1782+* `events` section.
1783+* `BOOL_CLAUSE` of a conditional in the [outcome section](#outcome_section_syntax).
1784+
1785+### arrays.concat
1786+
1787+Supported in:
1788+
1789+[Rules](/chronicle/docs/detection/default-rules)
1790+[Search](/chronicle/docs/investigation/udm-search)
1791+
1792+```
1793+arrays.concat(string_array, string_array)
1794+
1795+```
1796+
1797+#### Description
1798+
1799+Returns a new string array by copying elements from original string arrays.
1800+
1801+#### Param data types
1802+
1803+`ARRAY_STRINGS`, `ARRAY_STRINGS`
1804+
1805+#### Return type
1806+
1807+`ARRAY_STRINGS`
1808+
1809+#### Code samples
1810+
1811+##### Example 1
1812+
1813+The following example concatenates two different string arrays.
1814+
1815+```
1816+arrays.concat(["test1", "test2"], ["test3"]) = ["test1", "test2", "test3"]
1817+
1818+```
1819+
1820+##### Example 2
1821+
1822+The following example concatenates arrays with empty string.
1823+
1824+```
1825+arrays.concat([""], [""]) = ["", ""]
1826+
1827+```
1828+
1829+##### Example 3
1830+
1831+The following example concatenates empty arrays.
1832+
1833+```
1834+arrays.concat([], []) = []
1835+
1836+```
1837+
1838+
1839+
1840+### arrays.join\_string
1841+
1842+Supported in:
1843+
1844+[Rules](/chronicle/docs/detection/default-rules)
1845+[Search](/chronicle/docs/investigation/udm-search)
1846+
1847+```
1848+arrays.join_string(array_of_strings, optional_delimiter)
1849+
1850+```
1851+
1852+#### Description
1853+
1854+Converts an array of strings into a single string separated by the optional parameter. If no delimiter is provided, the empty string is used.
1855+
1856+#### Param data types
1857+
1858+`ARRAY_STRINGS`, `STRING`
1859+
1860+#### Return type
1861+
1862+`STRING`
1863+
1864+#### Code samples
1865+
1866+Here are some examples of how to use the function:
1867+
1868+##### Example 1
1869+
1870+This example joins an array with non-null elements and a delimiter.
1871+
1872+```
1873+arrays.join_string(["foo", "bar"], ",") = "foo,bar"
1874+
1875+```
1876+
1877+##### Example 2
1878+
1879+This example joins an array with a null element and a delimiter.
1880+
1881+```
1882+arrays.join_string(["foo", NULL, "bar"], ",") = "foo,bar"
1883+
1884+```
1885+
1886+##### Example 3
1887+
1888+This example joins an array with non-null elements and no delimiter.
1889+
1890+```
1891+arrays.join_string(["foo", "bar"]) = "foobar"
1892+
1893+```
1894+
1895+
1896+
1897+### arrays.length
1898+
1899+Supported in:
1900+
1901+[Rules](/chronicle/docs/detection/default-rules)
1902+[Search](/chronicle/docs/investigation/udm-search)
1903+
1904+```
1905+arrays.length(repeatedField)
1906+
1907+```
1908+
1909+#### Description
1910+
1911+Returns the number of repeated field elements.
1912+
1913+#### Param data types
1914+
1915+`LIST`
1916+
1917+#### Return type
1918+
1919+`NUMBER`
1920+
1921+#### Code samples
1922+
1923+##### Example 1
1924+
1925+Returns the number of repeated field elements.
1926+
1927+```
1928+arrays.length($e.principal.ip) = 2
1929+
1930+```
1931+
1932+##### Example 2
1933+
1934+If multiple repeated fields are along the path, returns the total number of repeated field elements.
1935+
1936+```
1937+arrays.length($e.intermediary.ip) = 3
1938+
1939+```
1940+
1941+
1942+
1943+### arrays.max
1944+
1945+Supported in:
1946+
1947+[Rules](/chronicle/docs/detection/default-rules)
1948+[Search](/chronicle/docs/investigation/udm-search)
1949+
1950+```
1951+arrays.max(array_of_ints_or_floats)
1952+
1953+```
1954+
1955+#### Description
1956+
1957+Returns the greatest element in an array or zero if the array is empty.
1958+
1959+#### Param data types
1960+
1961+`ARRAY_INTS|ARRAY_FLOATS`
1962+
1963+#### Return type
1964+
1965+`FLOAT`
1966+
1967+#### Code samples
1968+
1969+Here are some examples of how to use the function:
1970+
1971+##### Example 1
1972+
1973+This example returns the greater element in an array of integers.
1974+
1975+```
1976+arrays.max([10, 20]) = 20.000000
1977+
1978+```
1979+
1980+##### Example 2
1981+
1982+This example returns the greater element in an array of floats.
1983+
1984+```
1985+arrays.max([10.000000, 20.000000]) = 20.000000
1986+
1987+```
1988+
1989+
1990+
1991+### arrays.min
1992+
1993+Supported in:
1994+
1995+[Rules](/chronicle/docs/detection/default-rules)
1996+[Search](/chronicle/docs/investigation/udm-search)
1997+
1998+```
1999+arrays.min(array_of_ints_or_floats[, ignore_zeros=false])
2000+
2001+```
2002+
2003+#### Description
2004+
2005+Returns the smallest element in an array or zero if the array is empty. If the
2006+second, optional argument is set to true, elements equal to zero are ignored.
2007+
2008+#### Param data types
2009+
2010+`ARRAY_INTS|ARRAY_FLOATS`, `BOOL`
2011+
2012+#### Return type
2013+
2014+`FLOAT`
2015+
2016+#### Code samples
2017+
2018+Here are some examples of how to use the function:
2019+
2020+##### Example 1
2021+
2022+This example returns the smallest element in an array of integers.
2023+
2024+```
2025+arrays.min([10, 20]) = 10.000000
2026+
2027+```
2028+
2029+##### Example 2
2030+
2031+This example returns the smallest element in an array of floats.
2032+
2033+```
2034+arrays.min([10.000000, 20.000000]) = 10.000000
2035+
2036+```
2037+
2038+##### Example 3
2039+
2040+This example returns the smallest element in an array of floats, while ignoring the zeroes.
2041+
2042+```
2043+arrays.min([10.000000, 20.000000, 0.0], true) = 10.000000
2044+
2045+```
2046+
2047+
2048+
2049+### arrays.size
2050+
2051+Supported in:
2052+
2053+[Rules](/chronicle/docs/detection/default-rules)
2054+[Search](/chronicle/docs/investigation/udm-search)
2055+
2056+```
2057+arrays.size( array )
2058+
2059+```
2060+
2061+#### Description
2062+
2063+Returns the size of the array. Returns 0 for an empty array.
2064+
2065+#### Param data types
2066+
2067+`ARRAY_STRINGS|ARRAY_INTS|ARRAY_FLOATS`
2068+
2069+#### Return type
2070+
2071+`INT`
2072+
2073+#### Code samples
2074+
2075+##### Example 1
2076+
2077+This example uses a string array that contains two elements.
2078+
2079+```
2080+arrays.size(["test1", "test2"]) = 2
2081+
2082+```
2083+
2084+##### Example 2
2085+
2086+This example uses an int array that contains 3 elements.
2087+
2088+```
2089+arrays.size([1, 2, 3]) = 3
2090+
2091+```
2092+
2093+##### Example 3
2094+
2095+This example uses a float array thats contains 1 elements
2096+
2097+```
2098+arrays.size([1.200000]) = 1
2099+
2100+```
2101+
2102+##### Example 4
2103+
2104+This example uses an empty array.
2105+
2106+```
2107+arrays.size([]) = 0
2108+
2109+```
2110+
2111+
2112+
2113+### arrays.index\_to\_float
2114+
2115+Supported in:
2116+
2117+[Rules](/chronicle/docs/detection/default-rules)
2118+[Search](/chronicle/docs/investigation/udm-search)
2119+
2120+```
2121+arrays.index_to_float(array, index)
2122+
2123+```
2124+
2125+#### Description
2126+
2127+Returns the element at the given index of an array. The element at that index is returned as a float.
2128+
2129+The index is an integer value which represents the position of an element in the array.
2130+By default, the first element of an array has an index of 0, and the last element has an index of n-1, where n is the size of the array.
2131+Negative indexing allows accessing array elements relative to the end of the array. For example, an index of -1 refers to the last element in the array and an index of -2 refers to the second to last element in the array.
2132+
2133+#### Param data types
2134+
2135+`ARRAY_STRINGS|ARRAY_INTS|ARRAY_FLOATS`, `INT`
2136+
2137+#### Return type
2138+
2139+`FLOAT`
2140+
2141+#### Code samples
2142+
2143+##### Example 1
2144+
2145+The following example fetches an element at index 1 from an array of floats.
2146+
2147+```
2148+arrays.index_to_float([1.2, 2.1, 3.5, 4.6], 1) // 2.1
2149+
2150+```
2151+
2152+##### Example 2
2153+
2154+The following example fetches an element at index -1 from an array of floats.
2155+
2156+```
2157+arrays.index_to_float([1.2, 2.1, 3.5, 4.6], 0-1) // 4.6
2158+
2159+```
2160+
2161+##### Example 3
2162+
2163+The following example fetches an element for an index greater than the size of the array.
2164+
2165+```
2166+arrays.index_to_float([1.2, 2.1, 3.5, 4.6], 6) // 0.0
2167+
2168+```
2169+
2170+##### Example 4
2171+
2172+The following example fetches an element from an empty array.
2173+
2174+```
2175+arrays.index_to_float([], 0) // 0.0
2176+
2177+```
2178+
2179+##### Example 5
2180+
2181+The following example fetches an element at index 1 from a string array.
2182+
2183+```
2184+arrays.index_to_float(["1.2", "3.3", "2.4"], 1) // 3.3
2185+
2186+```
2187+
2188+##### Example 6
2189+
2190+The following example fetches an element at index 2 from an array of integers.
2191+
2192+```
2193+arrays.index_to_float([1, 3, 2], 2) // 2.0
2194+
2195+```
2196+
2197+
2198+
2199+### arrays.index\_to\_int
2200+
2201+Supported in:
2202+
2203+[Rules](/chronicle/docs/detection/default-rules)
2204+[Search](/chronicle/docs/investigation/udm-search)
2205+
2206+```
2207+arrays.index_to_int(array_of_inputs, index)
2208+
2209+```
2210+
2211+#### Description
2212+
2213+Returns the value at a given index in an array as an integer.
2214+
2215+The index is an integer value which represents the position of an element in the array.
2216+By default, the first element of an array has an index of 0, and the last element has an index of n-1, where n is the size of the array.
2217+Negative indexing allows accessing array elements relative to the end of the array. For example, an index of -1 refers to the last element in the array and an index of -2 refers to the second to last element in the array.
2218+
2219+#### Param data types
2220+
2221+`ARRAY_STRINGS|ARRAY_INTS|ARRAY_FLOATS`, `INT`
2222+
2223+#### Return type
2224+
2225+`INT`
2226+
2227+#### Code samples
2228+
2229+##### Example 1
2230+
2231+This function call returns 0 when the value at the index is a non-numeric string.
2232+
2233+```
2234+arrays.index_to_int(["str0", "str1", "str2"], 1) = 0
2235+
2236+```
2237+
2238+##### Example 2
2239+
2240+This function returns the element at index -1.
2241+
2242+```
2243+arrays.index_to_int(["44", "11", "22", "33"], 0-1) = 33
2244+
2245+```
2246+
2247+##### Example 3
2248+
2249+Returns 0 for the out-of-bounds element.
2250+
2251+```
2252+arrays.index_to_int(["44", "11", "22", "33"], 5) = 0
2253+
2254+```
2255+
2256+##### Example 4
2257+
2258+This function fetches the element from the float array at index 1.
2259+
2260+```
2261+arrays.index_to_int([1.100000, 1.200000, 1.300000], 1) = 1
2262+
2263+```
2264+
2265+##### Example 5
2266+
2267+This function fetches the element from the int array at index 0.
2268+
2269+```
2270+arrays.index_to_int([1, 2, 3], 0) = 1
2271+
2272+```
2273+
2274+
2275+
2276+### arrays.index\_to\_str
2277+
2278+Supported in:
2279+
2280+[Rules](/chronicle/docs/detection/default-rules)
2281+[Search](/chronicle/docs/investigation/udm-search)
2282+
2283+```
2284+arrays.index_to_str(array, index)
2285+
2286+```
2287+
2288+#### Description
2289+
2290+Returns the element at the given index from the array as a string.
2291+The index is an integer value that represents the position of an element in the array.
2292+By default, the first element of an array has an index of 0, and the last element has an index of n-1, where n is the size of the array.
2293+Negative indexing allows accessing array elements from the end of the array. For example, an index of -1 refers to the last element in the array and an index of -2 refers to the second to last element in the array.
2294+
2295+#### Param data types
2296+
2297+`ARRAY_STRINGS|ARRAY_INTS|ARRAY_FLOATS`, `INT`
2298+
2299+#### Return type
2300+
2301+`STRING`
2302+
2303+#### Code samples
2304+
2305+##### Example 1
2306+
2307+The following example fetches an element at index 1 from an array of strings.
2308+
2309+```
2310+arrays.index_to_str(["test1", "test2", "test3", "test4"], 1) // "test2"
2311+
2312+```
2313+
2314+##### Example 2
2315+
2316+The following example fetches an element at index -1 (last element of the array)
2317+from an array of strings.
2318+
2319+```
2320+arrays.index_to_str(["test1", "test2", "test3", "test4"], 0-1) // "test4"
2321+
2322+```
2323+
2324+##### Example 3
2325+
2326+The following example fetches an element for an index greater than the size of the array, which returns an empty string.
2327+
2328+```
2329+arrays.index_to_str(["test1", "test2", "test3", "test4"], 6) // ""
2330+
2331+```
2332+
2333+##### Example 4
2334+
2335+The following example fetches an element from an empty array.
2336+
2337+```
2338+arrays.index_to_str([], 0) // ""
2339+
2340+```
2341+
2342+##### Example 5
2343+
2344+The following example fetches an element at index 0 from an array of floats. The output is returned as a string.
2345+
2346+```
2347+arrays.index_to_str([1.200000, 3.300000, 2.400000], 0) // "1.2"
2348+
2349+```
2350+
2351+##### Example 6
2352+
2353+The following example fetches an element at index 2 from an array of integers. The output is in the form of a string.
2354+
2355+```
2356+arrays.index_to_str([1, 3, 2], 2) // "2"
2357+
2358+```
2359+
2360+
2361+
2362+### cast.as\_bool
2363+
2364+Supported in:
2365+
2366+[Rules](/chronicle/docs/detection/default-rules)
2367+[Search](/chronicle/docs/investigation/udm-search)
2368+
2369+```
2370+cast.as_bool(string_or_int)
2371+
2372+```
2373+
2374+#### Description
2375+
2376+Function converts an int or string value into a bool value. Function calls with
2377+values that cannot be casted will return FALSE. Returns TRUE only for integer 1
2378+and case insensitive string 'true'.
2379+
2380+#### Param data types
2381+
2382+`INT|STRING`
2383+
2384+#### Return type
2385+
2386+`BOOL`
2387+
2388+#### Code samples
2389+
2390+##### Example 1
2391+
2392+This example shows how to cast a non-boolean string
2393+
2394+```
2395+cast.as_bool("123") = false
2396+
2397+```
2398+
2399+##### Example 2
2400+
2401+Truthy integer (1)
2402+
2403+```
2404+cast.as_bool(1) = true
2405+
2406+```
2407+
2408+##### Example 3
2409+
2410+Truthy string
2411+
2412+```
2413+cast.as_bool("true") = true
2414+
2415+```
2416+
2417+##### Example 4
2418+
2419+Capital truthy string
2420+
2421+```
2422+cast.as_bool("TRUE") = true
2423+
2424+```
2425+
2426+##### Example 5
2427+
2428+Negative integer
2429+
2430+```
2431+cast.as_bool(0-1) = false
2432+
2433+```
2434+
2435+##### Example 6
2436+
2437+False integer (0)
2438+
2439+```
2440+cast.as_bool(0) = false
2441+
2442+```
2443+
2444+##### Example 7
2445+
2446+empty string
2447+
2448+```
2449+cast.as_bool("") = false
2450+
2451+```
2452+
2453+
2454+
2455+### cast.as\_float
2456+
2457+Supported in:
2458+
2459+[Rules](/chronicle/docs/detection/default-rules)
2460+[Search](/chronicle/docs/investigation/udm-search)
2461+
2462+```
2463+cast.as_float(string_to_cast)
2464+
2465+```
2466+
2467+#### Description
2468+
2469+Converts a numeric string into a float. Any function calls with values that
2470+cannot be casted return 0. Floats maintain precision up to 7 decimal digits.
2471+
2472+#### Param data types
2473+
2474+`STRING`
2475+
2476+#### Return type
2477+
2478+`FLOAT`
2479+
2480+#### Code samples
2481+
2482+##### Example 1
2483+
2484+Casting a non-numeric string returns 0.
2485+
2486+```
2487+cast.as_float("str") = 0.0000000
2488+
2489+```
2490+
2491+##### Example 2
2492+
2493+Casting an empty string returns 0.
2494+
2495+```
2496+cast.as_float("") = 0.0000000
2497+
2498+```
2499+
2500+##### Example 3
2501+
2502+Casting a valid numeric string returns a float value.
2503+
2504+```
2505+cast.as_float("1.012345678") = 1.0123456
2506+
2507+```
2508+
2509+
2510+
2511+### cast.as\_string
2512+
2513+Supported in:
2514+
2515+[Rules](/chronicle/docs/detection/default-rules)
2516+[Search](/chronicle/docs/investigation/udm-search)
2517+
2518+```
2519+cast.as_string(int_or_bytes_or_bool, optional_default_string)
2520+
2521+```
2522+
2523+#### Description
2524+
2525+The `cast.as_string` function transforms an `INT`, `BYTES`, or `BOOL` value into its string representation. You can provide an optional `default_string` argument to handle cases where the cast fails. If you omit the `default_string` argument, or if the input is an invalid `UTF-8` or `BASE64` byte sequence, the function returns an empty string.
2526+
2527+#### Param data types
2528+
2529+`INT|BYTES|BOOL`, `STRING`
2530+
2531+#### Return type
2532+
2533+`STRING`
2534+
2535+#### Code samples
2536+
2537+##### Integer to String Conversion
2538+
2539+The function converts the integer `123` to the string `"123"`.
2540+
2541+```
2542+cast.as_string(123) = "123"
2543+
2544+```
2545+
2546+##### Float to String Conversion
2547+
2548+The function converts the float `2.25` to the string `"2.25"`.
2549+
2550+```
2551+cast.as_string(2.25) = "2.25"
2552+
2553+```
2554+
2555+##### Bytes to String Conversion
2556+
2557+The function converts the raw binary `b'01` to the string `"\x01"`.
2558+
2559+```
2560+cast.as_string(b'01, "") = "\x01"
2561+
2562+```
2563+
2564+##### Boolean to String Conversion
2565+
2566+The function converts the boolean `true` to the string `"true"`.
2567+
2568+```
2569+cast.as_string(true, "") = "true"
2570+
2571+```
2572+
2573+##### Failed Conversion (Defaults to the Optionally Provided String)
2574+
2575+The function defaults to the string `"casting error"` when the value provided is invalid.
2576+
2577+```
2578+cast.as_string(9223372036854775808, "casting error") = "casting error"
2579+
2580+```
2581+
2582+
2583+
2584+### fingerprint
2585+
2586+Supported in:
2587+
2588+[Rules](/chronicle/docs/detection/default-rules)
2589+
2590+```
2591+hash.fingerprint2011(byteOrString)
2592+
2593+```
2594+
2595+#### Description
2596+
2597+This function calculates the `fingerprint2011` hash of an input byte sequence
2598+or string. This function returns an unsigned `INT` value in the range `[2, 0xFFFFFFFFFFFFFFFF]`.
2599+
2600+**Note:** This function shouldn't be used as a cryptographic secure hash.
2601+
2602+#### Param data types
2603+
2604+`BTYE`, `STRING`
2605+
2606+#### Return type
2607+
2608+`INT`
2609+
2610+#### Code sample
2611+
2612+```
2613+id_fingerprint = hash.fingerprint2011("user123")
2614+
2615+```
2616+
2617+
2618+
2619+### group
2620+
2621+Supported in:
2622+
2623+[Search](/chronicle/docs/investigation/udm-search)
2624+
2625+```
2626+group(field1, field2, field3, ...)
2627+
2628+```
2629+
2630+#### Description
2631+
2632+Group fields of a similar type into a placeholder variable.
2633+
2634+In UDM search, [grouped
2635+fields](/chronicle/docs/investigation/udm-search#search_grouped_fields) are used to search across multiple fields of a similar type. The group
2636+function is similar to grouped fields except that it lets you select which fields you want
2637+grouped together to trigger a detection. You can use the group function for gathering information about a specific entity (for example, a hostname, IP address, or userid) across different [Noun types](/chronicle/docs/reference/udm-field-list#noun).
2638+
2639+**Note:** For search, you can use grouped fields in the events section, but not in
2640+the match and outcome sections.
2641+
2642+#### Code samples
2643+
2644+**Example 1**
2645+
2646+Group all the IP addresses together and provide a descending count of the most prevalent IP address in the time range scanned.
2647+
2648+```
2649+$ip = group(principal.ip, about.ip, target.ip)
2650+$ip != ""
2651+match:
2652+ $ip
2653+outcome:
2654+ $count = count_distinct(metadata.id)
2655+order:
2656+ $count desc
2657+
2658+```
2659+
2660+
2661+
2662+### hash.sha256
2663+
2664+Supported in:
2665+
2666+[Rules](/chronicle/docs/detection/default-rules)
2667+
2668+```
2669+hash.sha256(string)
2670+
2671+```
2672+
2673+#### Description
2674+
2675+Returns a SHA-256 hash of the input string.
2676+
2677+#### Param data types
2678+
2679+`STRING`
2680+
2681+#### Return type
2682+
2683+`STRING`
2684+
2685+#### Code samples
2686+
2687+##### Example 1
2688+
2689+This example shows the SHA-256 hash when the input is a valid string.
2690+
2691+```
2692+hash.sha256("str") = "8c25cb3686462e9a86d2883c5688a22fe738b0bbc85f458d2d2b5f3f667c6d5a"
2693+
2694+```
2695+
2696+##### Example 2
2697+
2698+This example shows the SHA-256 hash when the input is an empty string.
2699+
2700+```
2701+hash.sha256("") = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
2702+
2703+```
2704+
2705+
2706+
2707+### math.abs
2708+
2709+Supported in:
2710+
2711+[Rules](/chronicle/docs/detection/default-rules)
2712+[Search](/chronicle/docs/investigation/udm-search)
2713+
2714+```
2715+math.abs(numericExpression)
2716+
2717+```
2718+
2719+#### Description
2720+
2721+Returns the absolute value of an integer or float expression.
2722+
2723+#### Param data types
2724+
2725+`NUMBER`
2726+
2727+#### Return type
2728+
2729+`NUMBER`
2730+
2731+#### Code samples
2732+
2733+##### Example 1
2734+
2735+This example returns True if the event was more than 5 minutes from the time
2736+specified (in seconds from the Unix epoch), regardless of whether the event came
2737+before or after the time specified. A call to `math.abs` cannot depend on
2738+multiple variables or placeholders. For example, you cannot replace the
2739+hardcoded time value of 1643687343 in the following example with
2740+`$e2.metadata.event_timestamp.seconds`.
2741+
2742+```
2743+300 < math.abs($e1.metadata.event_timestamp.seconds - 1643687343)
2744+
2745+```
2746+
2747+
2748+
2749+### math.ceil
2750+
2751+Supported in:
2752+
2753+[Rules](/chronicle/docs/detection/default-rules)
2754+[Search](/chronicle/docs/investigation/udm-search)
2755+
2756+```
2757+math.ceil(number)
2758+
2759+```
2760+
2761+#### Description
2762+
2763+Returns the smallest integer that is not less than the given number (rounding up). Will return 0 if the input is null or too big to fit in an int64.
2764+
2765+#### Param data types
2766+
2767+`FLOAT`
2768+
2769+#### Return type
2770+
2771+`INT`
2772+
2773+#### Code samples
2774+
2775+This section contains examples of using `math.ceil`.
2776+
2777+##### Example 1
2778+
2779+This example returns the ceil of a whole number.
2780+
2781+```
2782+math.ceil(2.000000) = 2
2783+
2784+```
2785+
2786+##### Example 2
2787+
2788+This example returns the ceil of a negative number.
2789+
2790+```
2791+math.ceil(0-1.200000) = -1
2792+
2793+```
2794+
2795+##### Example 3
2796+
2797+This example returns 0 as the ceil of a number that is too big for a 64 bit integer.
2798+
2799+```
2800+math.ceil(184467440737095516160.0) = 0
2801+
2802+```
2803+
2804+
2805+
2806+### math.floor
2807+
2808+Supported in:
2809+
2810+[Rules](/chronicle/docs/detection/default-rules)
2811+[Search](/chronicle/docs/investigation/udm-search)
2812+
2813+```
2814+math.floor(float_val)
2815+
2816+```
2817+
2818+#### Description
2819+
2820+Returns the largest integer value that is not greater than the supplied value (rounding down). Returns 0 if the input is null or too large to fit into an int64.
2821+
2822+#### Param data types
2823+
2824+`FLOAT`
2825+
2826+#### Return type
2827+
2828+`INT`
2829+
2830+#### Code samples
2831+
2832+##### Example 1
2833+
2834+This example shows a positive number case.
2835+
2836+```
2837+math.floor(1.234568) = 1
2838+
2839+```
2840+
2841+##### Example 2
2842+
2843+This example shows a negative number case.
2844+
2845+```
2846+math.floor(0-1.234568) = -2
2847+
2848+```
2849+
2850+##### Example 3
2851+
2852+This example shows a zero case.
2853+
2854+```
2855+math.floor(0.000000) = 0
2856+
2857+```
2858+
2859+
2860+
2861+### math.geo\_distance
2862+
2863+Supported in:
2864+
2865+[Rules](/chronicle/docs/detection/default-rules)
2866+
2867+```
2868+math.geo_distance(longitude1, latitude1, longitude2, latitude2))
2869+
2870+```
2871+
2872+#### Description
2873+
2874+Returns the distance between two geographic locations (coordinates) in meters.
2875+Returns -1 if the coordinates are invalid.
2876+
2877+#### Parameter data types
2878+
2879+`FLOAT`, `FLOAT`, `FLOAT`, `FLOAT`
2880+
2881+#### Return type
2882+
2883+`FLOAT`
2884+
2885+#### Code samples
2886+
2887+##### Example 1
2888+
2889+The following example returns the distance when all parameters are valid
2890+coordinates:
2891+
2892+```
2893+math.geo_distance(-122.020287, 37.407574, -122.021810, 37.407574) = 134.564318
2894+
2895+```
2896+
2897+##### Example 2
2898+
2899+The following example returns the distance when one of the parameters is a
2900+truncated coordinate:
2901+
2902+```
2903+math.geo_distance(-122.000000, 37.407574, -122.021810, 37.407574) = 1926.421905
2904+
2905+```
2906+
2907+##### Example 3
2908+
2909+The following example returns `-1` when one of the parameters is an invalid
2910+coordinate:
2911+
2912+```
2913+math.geo_distance(0-122.897680, 37.407574, 0-122.021810, 97.407574) = -1.000000
2914+
2915+```
2916+
2917+##### Example 4
2918+
2919+The following example returns `0` when coordinates are the same:
2920+
2921+```
2922+math.geo_distance(-122.897680, 37.407574, -122.897680, 37.407574) = 0.000000
2923+
2924+```
2925+
2926+
2927+
2928+### math.is\_increasing
2929+
2930+Supported in:
2931+
2932+[Rules](/chronicle/docs/detection/default-rules)
2933+[Search](/chronicle/docs/investigation/udm-search)
2934+
2935+```
2936+math.is_increasing(num1, num2, num3)
2937+
2938+```
2939+
2940+#### Description
2941+
2942+Takes a list of numeric values (integers or doubles) and returns `True` if
2943+the values are in ascending order, and `False` otherwise.
2944+
2945+#### Param data types
2946+
2947+`INT|FLOAT`, `INT|FLOAT`, `INT|FLOAT`
2948+
2949+#### Return type
2950+
2951+`BOOL`
2952+
2953+#### Code samples
2954+
2955+##### Example 1
2956+
2957+This example includes timestamp-like values in seconds.
2958+
2959+```
2960+math.is_increasing(1716769112, 1716769113, 1716769114) = true
2961+
2962+```
2963+
2964+##### Example 2
2965+
2966+This example includes one negative double, one zero INT64, and one positive INT64 values.
2967+
2968+```
2969+math.is_increasing(-1.200000, 0, 3) = true
2970+
2971+```
2972+
2973+##### Example 3
2974+
2975+This example includes one negative double, one zero INT64, and one negative INT64 values.
2976+
2977+```
2978+math.is_increasing(0-1.200000, 0, 0-3) = false
2979+
2980+```
2981+
2982+##### Example 4
2983+
2984+This example includes two negative doubles and one zero INT64 value.
2985+
2986+```
2987+math.is_increasing(0-1.200000, 0-1.50000, 0) = false
2988+
2989+```
2990+
2991+##### Example 5
2992+
2993+This example includes one negative double and two values that are the same.
2994+
2995+```
2996+math.is_increasing(0-1.200000, 0, 0) = false
2997+
2998+```
2999+
3000+
3001+
3002+### math.log
3003+
3004+Supported in:
3005+
3006+[Rules](/chronicle/docs/detection/default-rules)
3007+[Search](/chronicle/docs/investigation/udm-search)
3008+
3009+```
3010+math.log(numericExpression)
3011+
3012+```
3013+
3014+#### Description
3015+
3016+Returns the natural log value of an integer or float expression.
3017+
3018+#### Param data types
3019+
3020+`NUMBER`
3021+
3022+#### Return type
3023+
3024+`NUMBER`
3025+
3026+#### Code samples
3027+
3028+##### Example 1
3029+
3030+```
3031+math.log($e1.network.sent_bytes) > 20
3032+
3033+```
3034+
3035+
3036+
3037+### math.pow
3038+
3039+Supported in:
3040+
3041+[Rules](/chronicle/docs/detection/default-rules)
3042+[Search](/chronicle/docs/investigation/udm-search)
3043+
3044+```
3045+math.pow(base, exponent)
3046+
3047+```
3048+
3049+#### Description
3050+
3051+Returns the value of the first arg raised to the power of the second arg. Returns 0 in case of overflow.
3052+
3053+#### Param data types
3054+
3055+base: `INT|FLOAT`
3056+exponent: `INT|FLOAT`
3057+
3058+#### Return type
3059+
3060+`FLOAT`
3061+
3062+#### Code samples
3063+
3064+##### Example 1
3065+
3066+This example shows an integer case.
3067+
3068+```
3069+math.pow(2, 2) // 4.00
3070+
3071+```
3072+
3073+##### Example 2
3074+
3075+This example shows a fraction base case.
3076+
3077+```
3078+math.pow(2.200000, 3) // 10.648
3079+
3080+```
3081+
3082+##### Example 3
3083+
3084+This example shows a fraction base and power case.
3085+
3086+```
3087+math.pow(2.200000, 1.200000) // 2.575771
3088+
3089+```
3090+
3091+##### Example 4
3092+
3093+This example shows a negative power case.
3094+
3095+```
3096+math.pow(3, 0-3) // 0.037037
3097+
3098+```
3099+
3100+##### Example 5
3101+
3102+This example shows a fraction power case.
3103+
3104+```
3105+math.pow(3, 0-1.200000) // 0.267581
3106+
3107+```
3108+
3109+##### Example 6
3110+
3111+This example shows a negative base case.
3112+
3113+```
3114+math.pow(0-3, 0-3) // -0.037037
3115+
3116+```
3117+
3118+##### Example 7
3119+
3120+This example shows a zero base case.
3121+
3122+```
3123+math.pow(0, 3) // 0
3124+
3125+```
3126+
3127+##### Example 8
3128+
3129+This example shows a zero power case.
3130+
3131+```
3132+math.pow(9223372036854775807, 0) // 1
3133+
3134+```
3135+
3136+##### Example 9
3137+
3138+This example shows a large base case.
3139+
3140+```
3141+math.pow(9223372036854775807, 1.200000) // 57262152889751593549824
3142+
3143+```
3144+
3145+
3146+
3147+### math.random
3148+
3149+Supported in:
3150+
3151+[Rules](/chronicle/docs/detection/default-rules)
3152+[Search](/chronicle/docs/investigation/udm-search)
3153+
3154+```
3155+math.random()
3156+
3157+```
3158+
3159+#### Description
3160+
3161+Generates a pseudo-random value of type DOUBLE in the range of `[0, 1)`, inclusive of 0 and exclusive of 1.
3162+
3163+#### Return type
3164+
3165+`FLOAT`
3166+
3167+#### Code samples
3168+
3169+The following example checks whether the random value is in the range `[0, 1)`.
3170+`none
3171+if(math.random() >= 0 and math.random() < 1) = true`
3172+
3173+### math.round
3174+
3175+Supported in:
3176+
3177+[Search](/chronicle/docs/investigation/udm-search)
3178+
3179+```
3180+math.round(numericExpression, decimalPlaces)
3181+
3182+```
3183+
3184+#### Description
3185+
3186+Returns a value rounded to the nearest integer or to the specified number of decimal places.
3187+
3188+#### Param data types
3189+
3190+`NUMBER`
3191+
3192+#### Return type
3193+
3194+`NUMBER`
3195+
3196+#### Code samples
3197+
3198+```
3199+math.round(10.7) // returns 11
3200+math.round(1.2567, 2) // returns 1.25
3201+math.round(0-10.7) // returns -11
3202+math.round(0-1.2) // returns -1
3203+math.round(4) // returns 4, math.round(integer) returns the integer
3204+
3205+```
3206+
3207+
3208+
3209+### math.sqrt
3210+
3211+Supported in:
3212+
3213+[Rules](/chronicle/docs/detection/default-rules)
3214+[Search](/chronicle/docs/investigation/udm-search)
3215+
3216+```
3217+math.sqrt(number)
3218+
3219+```
3220+
3221+#### Description
3222+
3223+Returns the square root of the given number. Returns 0 in case of negative numbers.
3224+
3225+#### Param data types
3226+
3227+`INT|FLOAT`
3228+
3229+#### Return type
3230+
3231+`FLOAT`
3232+
3233+#### Code samples
3234+
3235+##### Example 1
3236+
3237+This example returns the square root of an int argument.
3238+
3239+```
3240+math.sqrt(3) = 1.732051
3241+
3242+```
3243+
3244+##### Example 2
3245+
3246+This example returns the square root of a negative int argument.
3247+
3248+```
3249+math.sqrt(-3) = 0.000000
3250+
3251+```
3252+
3253+##### Example 3
3254+
3255+This example returns the square root of zero argument.
3256+
3257+```
3258+math.sqrt(0) = 0.000000
3259+
3260+```
3261+
3262+##### Example 4
3263+
3264+This example returns the square root of a float argument.
3265+
3266+```
3267+math.sqrt(9.223372) = 3.037000
3268+
3269+```
3270+
3271+##### Example 5
3272+
3273+This example returns the square root of a negative float argument.
3274+
3275+```
3276+math.sqrt(0-1.200000) = 0.000000
3277+
3278+```
3279+
3280+
3281+
3282+### metrics
3283+
3284+Supported in:
3285+
3286+[Rules](/chronicle/docs/detection/default-rules)
3287+
3288+Metrics functions can aggregate large amounts of historical data. You can use
3289+this in your rule using `metrics.functionName()` in the outcome
3290+section.
3291+
3292+For more information, see [YARA-L Metrics](/chronicle/docs/detection/metrics-functions).
3293+
3294+### net.ip\_in\_range\_cidr
3295+
3296+Supported in:
3297+
3298+[Rules](/chronicle/docs/detection/default-rules)
3299+[Search](/chronicle/docs/investigation/udm-search)
3300+
3301+```
3302+net.ip_in_range_cidr(ipAddress, subnetworkRange)
3303+
3304+```
3305+
3306+#### Description
3307+
3308+Returns `true` when the given IP address is within the specified subnetwork.
3309+
3310+You can use YARA-L to search for UDM events across all of the IP addresses
3311+within a subnetwork using the `net.ip_in_range_cidr()` statement.
3312+Both IPv4 and IPv6 are supported.
3313+
3314+To search across a range of IP addresses, specify an IP UDM field and a CIDR
3315+range. YARA-L can handle both singular and repeating IP address fields.
3316+
3317+To search across a range of IP addresses, specify an `ip` UDM field and a Classless Inter-Domain Routing (CIDR) range. YARA-L can handle both singular and repeating IP address fields.
3318+
3319+#### Param data types
3320+
3321+`STRING`, `STRING`
3322+
3323+#### Return type
3324+
3325+`BOOL`
3326+
3327+#### Code samples
3328+
3329+##### Example 1
3330+
3331+IPv4 example:
3332+
3333+```
3334+net.ip_in_range_cidr($e.principal.ip, "192.0.2.0/24")
3335+
3336+```
3337+
3338+##### Example 2
3339+
3340+IPv6 example:
3341+
3342+```
3343+net.ip_in_range_cidr($e.network.dhcp.yiaddr, "2001:db8::/32")
3344+
3345+```
3346+
3347+For an example rule using the `net.ip_in_range_cidr()`statement, see the example rule in [Single Event within Range of IP Addresses](/chronicle/docs/detection/yara-l-2-0-overview#single_event_within_range_of_ip_addresses).)
3348+
3349+### re.regex
3350+
3351+Supported in:
3352+
3353+[Rules](/chronicle/docs/detection/default-rules)
3354+[Search](/chronicle/docs/investigation/udm-search)
3355+
3356+You can define regular expression matching in YARA-L 2.0 using either of the following syntax:
3357+
3358+* Using YARA-L syntax — Related to events.
3359+ The following is a generic representation of this syntax:
3360+
3361+ ```
3362+ $e.field = /regex/
3363+
3364+ ```
3365+* Using YARA-L syntax — As a function taking in the following parameters:
3366+
3367+ + Field the regular expression is applied to.
3368+ + Regular expression specified as a string.
3369+
3370+ The following is a generic representation of this syntax:
3371+
3372+ ```
3373+ re.regex($e.field, `regex`)
3374+
3375+ ```
3376+
3377+#### Description
3378+
3379+This function returns `true` if the string contains a substring that matches the regular expression provided. It is unnecessary to add `.*` to the beginning or at the end of the regular expression.
3380+
3381+##### Notes
3382+
3383+* To match the exact string or only a prefix or suffix, include the `^`
3384+ (starting) and `$` (ending) anchor characters in the regular expression.
3385+ For example, `/^full$/` matches `"full"` exactly, while `/full/` could match
3386+ `"fullest"`, `"lawfull"`, and `"joyfully"`.
3387+* If the UDM field includes newline characters, the `regexp` only matches the
3388+ first line of the UDM field. To enforce full UDM field matching, add a `(?s)` to
3389+ the regular expression. For example, replace `/.*allUDM.*/` with
3390+ `/(?s).*allUDM.*/`.
3391+* You can use the `nocase` modifier after strings to indicate that the search
3392+ should ignore capitalization.
3393+
3394+#### Param data types
3395+
3396+`STRING`, `STRING`
3397+
3398+#### Param expression types
3399+
3400+`ANY`, `ANY`
3401+
3402+#### Return type
3403+
3404+`BOOL`
3405+
3406+#### Code samples
3407+
3408+##### Example 1
3409+
3410+```
3411+// Equivalent to $e.principal.hostname = /google/
3412+re.regex($e.principal.hostname, "google")
3413+
3414+```
3415+
3416+
3417+
3418+### re.capture
3419+
3420+Supported in:
3421+
3422+[Rules](/chronicle/docs/detection/default-rules)
3423+[Search](/chronicle/docs/investigation/udm-search)
3424+
3425+```
3426+re.capture(stringText, regex)
3427+
3428+```
3429+
3430+#### Description
3431+
3432+Captures (extracts) data from a string using the regular expression pattern
3433+provided in the argument.
3434+
3435+This function takes two arguments:
3436+
3437+* `stringText`: the original string to search.
3438+* `regex`: the regular expression indicating the pattern to search for.
3439+
3440+The regular expression can contain 0 or 1 capture groups in parentheses. If the
3441+regular expression contains 0 capture groups, the function returns the first
3442+entire matching substring. If the regular expression contains 1 capture group,
3443+it returns the first matching substring for the capture group. Defining two or
3444+more capture groups returns a compiler error.
3445+
3446+#### Param data types
3447+
3448+`STRING`, `STRING`
3449+
3450+#### Return type
3451+
3452+`STRING`
3453+
3454+#### Code samples
3455+
3456+##### Example 1
3457+
3458+In this example, if `$e.principal.hostname` contains "aaa1bbaa2" the following would be true, because the function
3459+returns the first instance. This example has no capture groups.
3460+
3461+```
3462+"aaa1" = re.capture($e.principal.hostname, "a+[1-9]")
3463+
3464+```
3465+
3466+##### Example 2
3467+
3468+This example captures everything after the @ symbol in an email. If the
3469+`$e.network.email.from` field is `test@google.com`, the example returns
3470+`google.com`. The following example contains one capture group.
3471+
3472+```
3473+"google.com" = re.capture($e.network.email.from , "@(.*)")
3474+
3475+```
3476+
3477+##### Example 3
3478+
3479+If the regular expression does not match any substring in the text, the
3480+function returns an empty string. You can omit events where no match occurs
3481+by excluding the empty string, which is especially important when you are
3482+using `re.capture()` with an inequality:
3483+
3484+```
3485+// Exclude the empty string to omit events where no match occurs.
3486+"" != re.capture($e.network.email.from , "@(.*)")
3487+
3488+// Exclude a specific string with an inequality.
3489+"google.com" != re.capture($e.network.email.from , "@(.*)")
3490+
3491+```
3492+
3493+
3494+
3495+### re.replace
3496+
3497+Supported in:
3498+
3499+[Rules](/chronicle/docs/detection/default-rules)
3500+[Search](/chronicle/docs/investigation/udm-search)
3501+
3502+```
3503+re.replace(stringText, replaceRegex, replacementText)
3504+
3505+```
3506+
3507+#### Description
3508+
3509+Performs a regular expression replacement.
3510+
3511+This function takes three arguments:
3512+
3513+* `stringText`: the original string.
3514+* `replaceRegex`: the regular expression indicating the pattern to search for.
3515+* `replacementText`: The text to insert into each match.
3516+
3517+Returns a new string derived from the original `stringText`, where all
3518+substrings that match the pattern in `replaceRegex` are replaced with the value in
3519+`replacementText`. You can use backslash-escaped digits (`\1` to `\9`) within
3520+`replacementText` to insert text matching the corresponding parenthesized group
3521+in the `replaceRegex` pattern. Use `\0` to refer to the entire matching text.
3522+
3523+The function replaces non-overlapping matches and will prioritize replacing the
3524+first occurrence found. For example, `re.replace("banana", "ana", "111")`
3525+returns the string "b111na".
3526+
3527+#### Param data types
3528+
3529+`STRING`, `STRING`, `STRING`
3530+
3531+#### Return type
3532+
3533+`STRING`
3534+
3535+#### Code samples
3536+
3537+##### Example 1
3538+
3539+This example captures everything after the `@` symbol in an email, replaces `com`
3540+with `org`, and then returns the result. Notice the use of nested functions.
3541+
3542+```
3543+"email@google.org" = re.replace($e.network.email.from, "com", "org")
3544+
3545+```
3546+
3547+##### Example 2
3548+
3549+This example uses backslash-escaped digits in the `replacementText` argument to
3550+reference matches to the `replaceRegex` pattern.
3551+
3552+```
3553+"test1.com.google" = re.replace(
3554+ $e.principal.hostname, // holds "test1.test2.google.com"
3555+ "test2\.([a-z]*)\.([a-z]*)",
3556+ "\\2.\\1" // \\1 holds "google", \\2 holds "com"
3557+ )
3558+
3559+```
3560+
3561+##### Example 3
3562+
3563+Note the following cases when dealing with empty strings and `re.replace()`:
3564+
3565+Using empty string as `replaceRegex`:
3566+
3567+```
3568+// In the function call below, if $e.principal.hostname contains "name",
3569+// the result is: 1n1a1m1e1, because an empty string is found next to
3570+// every character in `stringText`.
3571+re.replace($e.principal.hostname, "", "1")
3572+
3573+```
3574+
3575+To replace an empty string, you can use `"^$"` as `replaceRegex`:
3576+
3577+```
3578+// In the function call below, if $e.principal.hostname contains the empty
3579+// string, "", the result is: "none".
3580+re.replace($e.principal.hostname, "^$", "none")
3581+
3582+```
3583+
3584+
3585+
3586+### sample\_rate
3587+
3588+Supported in:
3589+
3590+[Rules](/chronicle/docs/detection/default-rules)
3591+
3592+```
3593+optimization.sample_rate(byteOrString, rateNumerator, rateDenominator)
3594+
3595+```
3596+
3597+#### Description
3598+
3599+This function determines whether to include an event based on a deterministic
3600+sampling strategy. This function returns:
3601+
3602+* `true` for a fraction of input values, equivalent to (`rateNumerator` / `rateDenominator`),
3603+ indicating that the event should be included in the sample.
3604+* `false` indicating that the event shouldn't be included in the sample.
3605+
3606+This function is useful for optimization scenarios where you want to process
3607+only a subset of events. Equivalent to:
3608+
3609+```
3610+hash.fingerprint2011(byteOrString) % rateDenominator < rateNumerator
3611+
3612+```
3613+
3614+#### Param data types
3615+
3616+* byteOrString: Expression that evaluates to either a `BYTE` or `STRING`.
3617+* rateNumerator: 'INT'
3618+* rateDenominator: 'INT'
3619+
3620+#### Return type
3621+
3622+`BOOL`
3623+
3624+#### Code sample
3625+
3626+```
3627+events:
3628+ $e.metadata.event_type = "NETWORK_CONNECTION"
3629+ $asset_id = $e.principal.asset.asset_id
3630+ optimization.sample_rate($e.metadata.id, 1, 5) // Only 1 out of every 5 events
3631+
3632+ match:
3633+ $asset_id over 1h
3634+
3635+ outcome:
3636+ $event_count = count_distinct($e.metadata.id)
3637+ // estimate the usage by multiplying by the inverse of the sample rate
3638+ $usage_past_hour = sum(5.0 * $e.network.sent_bytes)
3639+
3640+ condition:
3641+ // Requiring a certain number of events after sampling avoids bias (e.g. a
3642+ // device with just 1 connection will still show up 20% of the time and
3643+ // if we multiply that traffic by 5, we'll get an incorrect estimate)
3644+ $e and ($usage_past_hour > 1000000000) and $event_count >= 100
3645+
3646+```
3647+
3648+
3649+
3650+### strings.base64\_decode
3651+
3652+Supported in:
3653+
3654+[Rules](/chronicle/docs/detection/default-rules)
3655+[Search](/chronicle/docs/investigation/udm-search)
3656+
3657+```
3658+strings.base64_decode(encodedString)
3659+
3660+```
3661+
3662+#### Description
3663+
3664+Returns a string containing the base64 decoded version of the encoded string.
3665+
3666+This function takes one base64 encoded string as an argument. If `encodedString`
3667+is not a valid base64 encoded string, the function returns `encodedString` unchanged.
3668+
3669+#### Param data types
3670+
3671+`STRING`
3672+
3673+#### Return type
3674+
3675+`STRING`
3676+
3677+#### Code samples
3678+
3679+##### Example 1
3680+
3681+```
3682+"test" = strings.base64_decode($e.principal.domain.name)
3683+
3684+```
3685+
3686+
3687+
3688+### strings.coalesce
3689+
3690+Supported in:
3691+
3692+[Rules](/chronicle/docs/detection/default-rules)
3693+[Search](/chronicle/docs/investigation/udm-search)
3694+
3695+```
3696+strings.coalesce(a, b, c, ...)
3697+
3698+```
3699+
3700+#### Description
3701+
3702+This function takes an unlimited number of arguments and returns the value of the first expression that does not evaluate to an empty string (for example, "non-zero value"). If all arguments evaluate to an empty string, the function call returns an empty string.
3703+
3704+The arguments can be literals, event fields, or function calls. All arguments must be of `STRING` type. If any arguments are event fields, the attributes must be from the same event.
3705+
3706+#### Param data types
3707+
3708+`STRING`
3709+
3710+#### Return type
3711+
3712+`STRING`
3713+
3714+#### Code samples
3715+
3716+##### Example 1
3717+
3718+The following example includes string variables as arguments. The condition
3719+evaluates to true when (1) `$e.network.email.from` is `suspicious@gmail.com` or
3720+(2) `$e.network.email.from` is empty and `$e.network.email.to` is
3721+`suspicious@gmail.com`.
3722+
3723+```
3724+"suspicious@gmail.com" = strings.coalesce($e.network.email.from, $e.network.email.to)
3725+
3726+```
3727+
3728+##### Example 2
3729+
3730+The following example calls the `coalesce` function with more than two
3731+arguments. This condition compares the first non-null IP address from event `$e`
3732+against values in the reference list `ip_watchlist`. The order that the
3733+arguments are coalesced in this call is the same as the order they are
3734+enumerated in the rule condition:
3735+
3736+1. `$e.principal.ip` is evaluated first.
3737+2. `$e.src.ip` is evaluated next.
3738+3. `$e.target.ip` is evaluated next.
3739+4. Finally, the string "No IP" is returned as a default value if the previous `ip`
3740+ fields are unset.
3741+
3742+```
3743+strings.coalesce($e.principal.ip, $e.src.ip, $e.target.ip, "No IP") in %ip_watchlist
3744+
3745+```
3746+
3747+##### Example 3
3748+
3749+The following example attempts to coalesce `principal.hostname` from event
3750+`$e1` and event `$e2`. It will return a compiler error because the arguments are
3751+different event variables.
3752+
3753+```
3754+// returns a compiler error
3755+"test" = strings.coalesce($e1.principal.hostname, $e2.principal.hostname)
3756+
3757+```
3758+
3759+
3760+
3761+### strings.concat
3762+
3763+Supported in:
3764+
3765+[Rules](/chronicle/docs/detection/default-rules)
3766+[Search](/chronicle/docs/investigation/udm-search)
3767+
3768+```
3769+strings.concat(a, b, c, ...)
3770+
3771+```
3772+
3773+#### Description
3774+
3775+Returns the concatenation of an unlimited number of items, each of which can be
3776+a string, integer, or float.
3777+
3778+If any arguments are event fields, the attributes must be from the same event.
3779+
3780+#### Param data types
3781+
3782+`STRING`, `FLOAT`, `INT`
3783+
3784+#### Return type
3785+
3786+`STRING`
3787+
3788+#### Code samples
3789+
3790+##### Example 1
3791+
3792+The following example includes a string variable and integer variable as
3793+arguments. Both `principal.hostname` and `principal.port` are from the same
3794+event, `$e`, and are concatenated to return a string.
3795+
3796+```
3797+"google:80" = strings.concat($e.principal.hostname, ":", $e.principal.port)
3798+
3799+```
3800+
3801+##### Example 2
3802+
3803+The following example includes a string variable and string literal as arguments.
3804+
3805+```
3806+"google-test" = strings.concat($e.principal.hostname, "-test") // Matches the event when $e.principal.hostname = "google"
3807+
3808+```
3809+
3810+##### Example 3
3811+
3812+The following example includes a string variable and float literal as arguments.
3813+When represented as strings, floats that are whole numbers are formatted without
3814+the decimal point (for example, 1.0 is represented as "1"). Additionally,
3815+floats that exceed sixteen decimal digits are truncated to the sixteenth decimal
3816+place.
3817+
3818+```
3819+"google2.5" = strings.concat($e.principal.hostname, 2.5)
3820+
3821+```
3822+
3823+##### Example 4
3824+
3825+The following example includes a string variable, string literal,
3826+integer variable, and float literal as arguments. All variables are from the
3827+same event, `$e`, and are concatenated with the literals to return a string.
3828+
3829+```
3830+"google-test802.5" = strings.concat($e.principal.hostname, "-test", $e.principal.port, 2.5)
3831+
3832+```
3833+
3834+##### Example 5
3835+
3836+The following example attempts to concatenate principal.port from event `$e1`,
3837+with `principal.hostname` from event `$e2`. It will return a compiler error
3838+because the arguments are different event variables.
3839+
3840+```
3841+// Will not compile
3842+"test" = strings.concat($e1.principal.port, $e2.principal.hostname)
3843+
3844+```
3845+
3846+
3847+
3848+### strings.contains
3849+
3850+Supported in:
3851+
3852+[Rules](/chronicle/docs/detection/default-rules)
3853+[Search](/chronicle/docs/investigation/udm-search)
3854+
3855+```
3856+strings.contains( str, substr )
3857+
3858+```
3859+
3860+#### Description
3861+
3862+Returns true if a given string contains the specified substring. Otherwise it returns false.
3863+
3864+#### Param data types
3865+
3866+`STRING`, `STRING`
3867+
3868+#### Return type
3869+
3870+`BOOL`
3871+
3872+#### Code samples
3873+
3874+##### Example 1
3875+
3876+This example returns true because the string has a substring "is".
3877+
3878+```
3879+strings.contains("thisisastring", "is") = true
3880+
3881+```
3882+
3883+##### Example 2
3884+
3885+This example returns false because the string does not have substring "that".
3886+
3887+```
3888+strings.contains("thisisastring", "that") = false
3889+
3890+```
3891+
3892+
3893+
3894+### strings.count\_substrings
3895+
3896+Supported in:
3897+
3898+[Rules](/chronicle/docs/detection/default-rules)
3899+[Search](/chronicle/docs/investigation/udm-search)
3900+
3901+```
3902+strings.count_substrings(string_to_search_in, substring_to_count)
3903+
3904+```
3905+
3906+#### Description
3907+
3908+When given a string and a substring, returns an int64 of the count of non-overlapping occurrences of the substring within the string.
3909+
3910+#### Param data types
3911+
3912+`STRING`, `STRING`
3913+
3914+#### Return type
3915+
3916+`INT`
3917+
3918+#### Code samples
3919+
3920+This section contains examples that calculate the number of times a substring appears in a given string.
3921+
3922+##### Example 1
3923+
3924+This example uses a non-null string and a non-null single substring character.
3925+
3926+```
3927+strings.count_substrings("this`string`has`four`backticks", "`") = 4
3928+
3929+```
3930+
3931+##### Example 2
3932+
3933+This example uses a non-null string and a non-null substring greater than one character.
3934+
3935+```
3936+strings.count_substrings("str", "str") = 1
3937+
3938+```
3939+
3940+##### Example 3
3941+
3942+This example uses a non-null string and an empty substring.
3943+
3944+```
3945+strings.count_substrings("str", "") = 0
3946+
3947+```
3948+
3949+##### Example 4
3950+
3951+This example uses an empty string and a non-null substring greater than one character.
3952+
3953+```
3954+strings.count_substrings("", "str") = 0
3955+
3956+```
3957+
3958+##### Example 5
3959+
3960+This example uses an empty string and an empty substring.
3961+
3962+```
3963+strings.count_substrings("", "") = 0
3964+
3965+```
3966+
3967+##### Example 6
3968+
3969+This example uses a non-null string and a non-null substring that is greater than one character and greater than one occurrence.
3970+
3971+```
3972+strings.count_substrings("fooABAbarABAbazABA", "AB") = 3
3973+
3974+```
3975+
3976+##### Example 7
3977+
3978+This example uses a non-null string and a non-null substring that is greater than one character and greater than one occurrence. It highlights the limitation with overlapping substring occurrences
3979+
3980+```
3981+strings.count_substrings("ABABABA", "ABA") = 2
3982+
3983+```
3984+
3985+
3986+
3987+### strings.extract\_domain
3988+
3989+Supported in:
3990+
3991+[Rules](/chronicle/docs/detection/default-rules)
3992+[Search](/chronicle/docs/investigation/udm-search)
3993+
3994+```
3995+strings.extract_domain(url_string)
3996+
3997+```
3998+
3999+#### Description
574000
