Expr Rules for Sandfly

An expr rule has access to the result data object(s) that the selected Sandfly forensic engine provides. The user engine will make available the "user" object to the expr rules, the file engine will make available the "file" object, etc.

For example, if you are looking at process recon results in sandfly and see the {"results": {"process": {"name": "apache"}}} field in the raw result data, you can write an expr rule that matches that process by referring to the "name" property of the "process" result structure as:

process.name == 'apache'

For result data fields that are arrays which may contain multiple values, the expr language includes functions such as all, any, and none to look for results that do or do not include criteria that you are looking for:

any(process.network_ports.tcp.connections, {.port_local in [1337,4444,31337]})

A different array example that uses the predicate:

any(user.group_membership, {# == 'sudo'})"

Additional Operator Examples

This section contains further rule samples that are used in actual sandflies. Looking at the rules of a similarly functioning sandfly is an effective way to get started.

  • Comparison: < (less than)
user.password.age_min < 7
  • Comparison: != (not equals) + Logical: or
user.uid != 65534 or user.gid != 65534
  • Membership: in + Range: ..
log.wtmp.date.created_minutes in 1..1440
  • Membership: in + Membership: [ ] (array)
file.username in ['systemd-network','systemd-resolve','systemd-timesync']
  • Regex: matches + Logical: not
process.name not matches '(apache.*|nginx.*)'
  • String: startsWith as any part of an array
any(atjob.command, {# startsWith '/dev/shm/'})
  • Operator examples taken from actual Sandflies:
log.lastlog.date.created_minutes < 1440 && log.lastlog.username == 'bin'
process.network_ports.operating == true and process.cmdline matches '\\\\btwistd.*--path[[:blank:]]*\\\\.'


The rule_op property controls whether all of the rules are combined with "and" or "or" logic, but if you need more complex nesting of logic, individual rules may contain multiple expressions combined with logical operators.

For example, to write a sandfly that matches an old process named "oldproc" or a new process named "newproc", you could use the following rules:

    "rule_op": "or",
    "rules": [
        "process.name == 'oldproc' && process.date.created_minutes > 1440",
        "process.name == 'newproc' && process.date.created_minutes < 1440"