Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Event types

An event type determines which runtime event a rule evaluates. Every event type can use process.

rule_sets:
  - ruleset_id: acme/process
    rules:
      - rule_id: network_tool_exec
        event_type: process_exec
        condition: |
          process.exec_path.endsWith("/curl") ||
          process.exec_path.endsWith("/wget")
        action: collect

process is the snapshot of the process that produced the event.

fieldTypeExample valueMeaning
process.exec_pathstring/usr/bin/curlExecutable path
process.argvlist(string)["curl", "-fsSL", "https://example.com/install.sh"]Process arguments
process.ancestorslist(object)[{exec_path: "/bin/bash", argv: ["bash", "-c", "npm install"]}]Snapshot of ancestor processes

process.ancestors is newest-first. The first element is the immediate parent, followed by the grandparent. Rule conditions should search ancestors with exists instead of index access. Each ancestor exposes exec_path, argv, and descendants. descendants contains only the processes forked below that ancestor on the path to the current process. It is ordered from that ancestor toward the current process: parent -> child. It does not include the current process itself.

condition: |
  process.ancestors.exists(parent,
    parent.exec_path.endsWith("/bash") &&
    parent.argv.exists(arg, arg == "-c")
  )

process_exec

Evaluates process execution.

fieldTypeExample valueMeaning
processobjectprocess.exec_path == "/usr/bin/curl"Executed process
is_memfdbooltrue / falseTrue for memfd-backed execution

Network tool execution:

rule_sets:
  - ruleset_id: acme/process
    rules:
      - rule_id: network_tool_exec
        event_type: process_exec
        condition: |
          process.exec_path.endsWith("/curl") ||
          process.exec_path.endsWith("/wget") ||
          process.exec_path.endsWith("/nc")
        action: collect

Shell started as a descendant of an installer or package manager:

rule_sets:
  - ruleset_id: acme/process
    rules:
      - rule_id: shell_from_package_manager
        event_type: process_exec
        condition: |
          (
            process.exec_path.endsWith("/sh") ||
            process.exec_path.endsWith("/bash")
          ) &&
          process.ancestors.exists(parent,
            parent.exec_path.endsWith("/npm") ||
            parent.exec_path.endsWith("/pip") ||
            parent.exec_path.endsWith("/bundle")
          )
        action: detect

memfd-backed execution:

condition: is_memfd

Example event value:

{
  "event_type": "process_exec",
  "process": {
    "exec_path": "/usr/bin/curl",
    "argv": ["curl", "-fsSL", "https://example.com/install.sh"],
    "ancestors": [
      {"exec_path": "/bin/bash", "argv": ["bash", "-c", "curl -fsSL https://example.com/install.sh"]}
    ]
  },
  "payload": {
    "is_memfd": false
  }
}

network_connect

Evaluates outbound network connections.

fieldTypeExample valueMeaning
remote_ipstring203.0.113.10, 2001:db8::1Destination IP address
remote_portint443, 53Destination port
protocolstringtcp, udpProtocol
familystringipv4, ipv6Destination address family
processobjectprocess.exec_path == "/usr/bin/curl"Process that created the connection

Outbound TCP connection from curl / wget:

rule_sets:
  - ruleset_id: acme/network
    rules:
      - rule_id: network_tool_outbound
        event_type: network_connect
        condition: |
          protocol == "tcp" &&
          remote_port == 443 &&
          (
            process.exec_path.endsWith("/curl") ||
            process.exec_path.endsWith("/wget")
          )
        action: collect

Connection to private networks:

condition: |
  family == "ipv4" &&
  (
    inIpRange(remote_ip, "10.0.0.0/8") ||
    inIpRange(remote_ip, "172.16.0.0/12") ||
    inIpRange(remote_ip, "192.168.0.0/16")
  )

Example event value:

{
  "event_type": "network_connect",
  "process": {
    "exec_path": "/usr/bin/curl",
    "argv": ["curl", "https://registry.npmjs.org/"]
  },
  "payload": {
    "remote_ip": "104.16.24.34",
    "remote_port": 443,
    "protocol": "tcp",
    "family": "ipv4"
  }
}

unix_socket_connect

Evaluates Unix domain socket connections.

fieldTypeExample valueMeaning
pathstring/run/docker.sock, @dbus-7Socket path. Abstract namespace sockets are represented as @....
socket_typestringstream, dgram, seqpacket, unknownSocket type
is_abstractbooltrue / falseTrue for abstract namespace sockets
processobjectprocess.exec_path == "/usr/bin/docker"Process that connected to the socket

Docker socket access:

rule_sets:
  - ruleset_id: acme/socket
    rules:
      - rule_id: docker_socket_access
        event_type: unix_socket_connect
        condition: |
          socket_type == "stream" &&
          !is_abstract &&
          (
            path == "/var/run/docker.sock" ||
            path == "/run/docker.sock"
          )
        action: detect

Example event value:

{
  "event_type": "unix_socket_connect",
  "process": {
    "exec_path": "/usr/bin/docker",
    "argv": ["docker", "ps"]
  },
  "payload": {
    "path": "/run/docker.sock",
    "socket_type": "stream",
    "is_abstract": false
  }
}

file_open

Evaluates file open events.

fieldTypeExample valueMeaning
pathstring/home/runner/.npmrc, /workspace/.envOpened file path
is_readbooltrue / falseTrue when read access is included
is_writebooltrue / falseTrue when write access is included
flagsint0, 66Open flags
processobjectprocess.exec_path == "/bin/cat"Process that opened the file

Credential file read:

rule_sets:
  - ruleset_id: acme/file
    rules:
      - rule_id: package_credential_read
        event_type: file_open
        condition: |
          is_read &&
          (
            path.endsWith("/.npmrc") ||
            path.endsWith("/.pypirc") ||
            path.endsWith("/.docker/config.json")
          )
        action: collect

Credential file read by a descendant of a shell:

condition: |
  is_read &&
  path.endsWith("/.npmrc") &&
  process.ancestors.exists(parent,
    parent.exec_path.endsWith("/sh") ||
    parent.exec_path.endsWith("/bash")
  )

Example event value:

{
  "event_type": "file_open",
  "process": {
    "exec_path": "/bin/cat",
    "argv": ["cat", "/home/runner/.npmrc"]
  },
  "payload": {
    "path": "/home/runner/.npmrc",
    "is_read": true,
    "is_write": false,
    "flags": 0
  }
}

file_remove

Evaluates file or directory removal.

fieldTypeExample valueMeaning
pathstring/workspace/.env, /var/log/journalRemoved path
is_folderbooltrue / falseTrue for directory removal
processobjectprocess.exec_path == "/bin/rm"Process that removed the path

Secret file removal:

condition: |
  !is_folder &&
  (
    path.endsWith("/.npmrc") ||
    path.endsWith("/.pypirc")
  )

Use !is_folder when you want to exclude directory removals and match only file unlink events.

Example event value:

{
  "event_type": "file_remove",
  "process": {
    "exec_path": "/bin/rm",
    "argv": ["rm", "/workspace/.env"]
  },
  "payload": {
    "path": "/workspace/.env",
    "is_folder": false
  }
}

file_move

Evaluates rename / move events.

fieldTypeExample valueMeaning
from_pathstring/tmp/payload.binOriginal path
to_pathstring/usr/local/bin/curlNew path
processobjectprocess.exec_path == "/bin/mv"Process that renamed / moved the path

Move from a temporary path to an executable path:

condition: |
  from_path.startsWith("/tmp/") &&
  (
    to_path.startsWith("/usr/local/bin/") ||
    to_path.startsWith("/usr/bin/")
  )

Example event value:

{
  "event_type": "file_move",
  "process": {
    "exec_path": "/bin/mv",
    "argv": ["mv", "/tmp/payload.bin", "/usr/local/bin/curl"]
  },
  "payload": {
    "from_path": "/tmp/payload.bin",
    "to_path": "/usr/local/bin/curl"
  }
}

Evaluates hardlink / symlink creation.

fieldTypeExample valueMeaning
created_pathstring/tmp/copy, /usr/local/bin/curlNewly created link / symlink path
existing_pathstring/etc/shadow, /tmp/wrapperExisting target path
is_hardlinkbooltrue / falseTrue for hardlinks
is_symlinkbooltrue / falseTrue for symlinks
processobjectprocess.exec_path == "/bin/ln"Process that created the link

Hardlink to /etc/shadow:

condition: is_hardlink && existing_path == "/etc/shadow"

Symlink from a temporary path to an executable path:

condition: |
  is_symlink &&
  created_path.startsWith("/usr/local/bin/") &&
  existing_path.startsWith("/tmp/")

Example event value:

{
  "event_type": "file_link",
  "process": {
    "exec_path": "/bin/ln",
    "argv": ["ln", "-s", "/tmp/wrapper", "/usr/local/bin/curl"]
  },
  "payload": {
    "created_path": "/usr/local/bin/curl",
    "existing_path": "/tmp/wrapper",
    "is_hardlink": false,
    "is_symlink": true
  }
}

mount

Evaluates mount attempts that may expose or relocate a mount tree through another path. The event records classic bind and move requests and new-API move_mount requests from tracked jobs. It does not represent every filesystem mount, and the recorded attempt is not guaranteed to have succeeded.

fieldTypeExample valueMeaning
source_pathstring/tmp/sourceMount source. For classic mount(2), this is the raw source string
target_pathstring/mnt/targetMount target path
processobjectprocess.exec_path == "/bin/mount"Process that attempted the mount

Because a classic source_path may be relative, symlinked, or otherwise non-canonical, source matching is a best-effort signal rather than a strict security boundary.

Protected path exposed through another location:

condition: source_path.startsWith("/protected/") && !target_path.startsWith("/protected/")

Example event value:

{
  "event_type": "mount",
  "process": {
    "exec_path": "/bin/mount",
    "argv": ["mount", "--bind", "/tmp/source", "/mnt/target"]
  },
  "payload": {
    "source_path": "/tmp/source",
    "target_path": "/mnt/target"
  }
}

The paths describe the mount operation, not a canonical identity for later file writes. The classic mount source is the raw source string supplied to the mount operation. Target paths and new-API move paths use a filesystem-rooted dentry walk and can expose node-side backing paths for Kubernetes volumes. A move_mount event can represent a detached mount attachment, a mount tree relocation, or another operation routed through the same kernel hook; rules should rely on path and process context rather than infer that distinction.

domain

Evaluates domain access.

fieldTypeExample valueMeaning
domainstringregistry.npmjs.org, example.comQuery domain. Lowercase, with trailing dot removed.
sourcestringdnsObservation source. Currently mainly dns.
processobjectprocess.exec_path == "/usr/bin/npm"Process that caused the domain access

Access outside known internal domains:

condition: |
  source == "dns" &&
  !domain.endsWith(".corp.example.com")

Suspicious domain access from a package-manager descendant:

condition: |
  source == "dns" &&
  process.ancestors.exists(parent,
    parent.exec_path.endsWith("/npm") ||
    parent.exec_path.endsWith("/pip")
  )

Example event value:

{
  "event_type": "domain",
  "process": {
    "exec_path": "/usr/bin/npm",
    "argv": ["npm", "install"]
  },
  "payload": {
    "domain": "registry.npmjs.org",
    "source": "dns"
  }
}

http_request

Evaluates the request line of an outgoing HTTP request: method, path, and host together. Only the request line and the Host header are captured — no other headers and no body. Capture is disabled by default during rollout. Enable the cleartext and uprobe-based sources together with --enable-http-request=true.

fieldTypeExample valueMeaning
methodstringget, postRequest method. Lowercase; write the condition in either case.
pathstring/repos/cli/cli/releasesRequest path with the query string removed. Lowercase.
hoststringapi.github.com, example.com:8080Request host (Host header). Lowercase; may include a port.
sourcestringcleartext_http, openssl, nghttp2, go_net_httpCapture channel.
processobjectprocess.exec_path == "/usr/bin/curl"Process that sent the request

source reports where the request line was read:

  • cleartext_http — plain HTTP (http://) traffic, including cloud metadata endpoints and plain-HTTP package mirrors.
  • openssl — HTTPS HTTP/1.x read before encryption at OpenSSL’s SSL_write / SSL_write_ex. Client coverage depends on which function the installed binary calls; see the developer guide’s verified workload matrix.
  • nghttp2 — HTTP/2 pseudo-headers read at nghttp2_submit_request or nghttp2_submit_request2, before HPACK encoding. This tap shares the same rollout state as openssl.
  • go_net_http — Go net/http request values read before protocol encoding. Stripped Go binaries are resolved through their pclntab metadata.

Known gaps (absence of an http_request event does not mean absence of egress — combine with domain and network_connect rules):

  • HTTP/2 clients that do not call a selected nghttp2 request function are not captured. HTTP/3/QUIC is also outside this event.
  • Java JSSE, rustls, and other stacks without a selected capture point are not captured.
  • Capture is best-effort against uncooperative code: a process can evade a uprobe, and the very first request of a brand-new process can beat the attach.

HTTP/1.x capture is best-effort at one write boundary. A path that crosses the 256-byte captured prefix is emitted only up to that boundary; a Host outside the prefix or too long for the field is empty. The nghttp2 tap does not emit a request whose method exceeds 15 bytes or whose path exceeds 255 bytes. A missing, invalid, or oversized HTTP/2 authority is emitted as an empty host.

Unexpected POST to the GitHub API:

condition: |
  method == "POST" &&
  host == "api.github.com" &&
  !process.exec_path.endsWith("/git")

Cloud metadata service access:

condition: |
  host == "169.254.169.254"

Example event value:

{
  "event_type": "http_request",
  "process": {
    "exec_path": "/usr/bin/curl",
    "argv": ["curl", "http://api.example.com/upload"]
  },
  "payload": {
    "method": "post",
    "path": "/upload",
    "host": "api.example.com",
    "source": "cleartext_http"
  }
}