![]() |
VOOZH | about |
This guide shows you how to write effective SECL (Security Language) rules for Datadog Workload Protection.
Datadog SECL is a custom domain-specific language used to create Agent expressions and policies within Datadog Workload Protection. SECL allows security teams to define real-time threat detection rules by specifying conditions, operators, and patterns that security agents can monitor across hosts, containers, applications, and cloud infrastructure.
Think of SECL as a local filter: it runs inside the Agent on each host, watching kernel and OS events. When an event matches your SECL expression, the Agent raises a detection.
Datadog threat detection rules act as backend logic: they combine one or more Agent rules (using @agent.rule_id), add thresholds, suppress noise, and decide how alerts are routed.
In summary, the Agent rule finds raw behavior and the detection rule turns it into a usable signal.
The standard format of a SECL expression is:
<event-type>.<event-attribute> <operator> <value> [<operator> <event-type>.<event-attribute>] ...Using this format, an example rule for a Linux system looks like this:
open.file.path == "/etc/shadow" && process.file.path not in ["/usr/sbin/vipw"]Here’s a summary of the process:
exec, open, connect, create, dns, etc.exec.file.path, process.ancestors.file.name.file.path, exec.args, open_key.registry.key_path.process.ancestors.*process.user, process.uidcontainer.*process.created_at > 5snot in [...], !~. In SECL, you can implement “allowlists” (the conditions that are included or excluded from detection) using ordinary operators like not in, !~, or conditional exclusions.To view Agent events that match the expression, view the rule details, and then click View Events. The Agent Events explorer opens and displays the Agent events that match the expression. In Agent Events, you can see the raw telemetry: every kernel/OS event that matched the SECL rule before suppression or aggregation.
When viewing raw Agent events, do the following:
To test detection rules, view the rule details, and then click [Number] detection rules found. The Detection Rules explorer opens and displays the backend logic layer: the detection rule, linked signals, metadata, JSON definition, tags, and version history.
When viewing the backend logic for a detection rule, do the following:
process.ancestors.file.name.> 5s, 10m, 2h) to target narrow execution windows.==) whenever possible as it results in the lowest noise.in [...]) is best for allowlists or controlled sets of values.~"/path/*") for path families as it is safer and faster than regex.=~) only when globs/lists can’t be used. Keep the regex expression as narrow as possible. As a rule of thumb, start with == or in [...]. Reach for regex only as a last resort.not in [...], !~) to carve out exceptions explicitly (for example, trusted tools).in CIDR, not in CIDR) for network boundaries.team, app, env, MITRE, severity.| Pattern | Explanation |
|---|---|
open.file.path == "/etc/passwd", exec.comm != "" | Too broad. Matches a lot of valid use cases. |
fd.name contains "/" | Matches nearly every file I/O event. |
container.id != "" | Useful only if scoped with a more specific field. |
For information and examples of common building blocks like operators, patterns, regular expressions, duration, and platform-specific syntax, see Creating Agent Rule Expressions.
open.file.path in ["/etc/shadow", "/etc/sudoers"] &&
process.file.path not in ["/usr/sbin/vipw", "/usr/sbin/visudo"]exec.file.path == "/usr/bin/bash" &&
(
process.ancestors.file.name == "nginx" ||
process.ancestors.file.name =~ "php*"
)connect &&
network.destination.ip in ["169.254.169.254"] &&
container.id != ""load_module &&
process.user != "root" &&
process.ancestors.file.name not in ["modprobe", "insmod"]open.file.path == "/etc/secret" &&
process.file.name == "java" &&
process.created_at > 5sconnect &&
network.destination.ip not in [10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12]set_key_value &&
open_key.registry.key_path =~ "*\\Software\\Microsoft\\Windows\\CurrentVersion\\Run*"exec.file.path =~ "*\\WindowsPowerShell\\v1.0\\powershell.exe" &&
process.parent.file.path !~ "*\\Program Files*" &&
process.user_sid != "S-1-5-18"exec.args_flags in ["cpu-priority", "donate-level", ~"randomx-1gb-pages"] ||
exec.args in [~"*stratum+tcp*", ~"*nicehash*", ~"*yespower*"]| Phase | Action |
|---|---|
| Draft | Author rule with YAML metadata and proper filters. |
| Simulate | Use Agent in read-only mode. |
| Validate | Run in staging workloads. Validate behavior with real events. |
| Tune | Add suppressions, not in, container.image, etc. |
| Scale | Reference in backend detection rules for full rollout. |
After validatation and deployment, continue monitoring rule performance to keep false positives low and coverage strong as workloads evolve.
| |