RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Diff/repulsivityy-elevate-2025-clinerules-readme ↔ repulsivityy-elevate-2025-clinerules-yaral-syntax

Comparison

A · Cline rules · repulsivityy/elevate_2025B · Cline rules · repulsivityy/elevate_2025
What each file covers, counted
DimensionSharedOnly in AOnly in BOverlap
Sections09580%
Commands000—
Section tags11320%

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 any

Section tags

1 shared · 1 only in A · 3 only in B
  • − setup
  • + types
  • + do-not
  • + docs
  •   architecture

Line diff

+3999 added−56 removed1 unchanged0.0% identical
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  
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack