scanner
  • About Scanner
  • When to use it
  • Architecture
  • Getting Started
  • Playground Guide
    • Overview
    • Part 1: Search and Analysis
    • Part 2: Detection Rules
    • Wrapping Up
  • Log Data Sources
    • Overview
    • List
      • AWS
        • AWS Aurora
        • AWS CloudTrail
        • AWS CloudWatch
        • AWS ECS
        • AWS EKS
        • AWS GuardDuty
        • AWS Lambda
        • AWS Route53 Resolver
        • AWS VPC Flow
        • AWS VPC Transit Gateway Flow
        • AWS WAF
      • Cloudflare
        • Audit Logs
        • Firewall Events
        • HTTP Requests
        • Other Datasets
      • Crowdstrike
      • Custom via Fluentd
      • Fastly
      • GitHub
      • Jamf
      • Lacework
      • Osquery
      • OSSEC
      • Sophos
      • Sublime Security
      • Suricata
      • Syslog
      • Teleport
      • Windows Defender
      • Windows Sysmon
      • Zeek
  • Indexing Your Logs in S3
    • Linking AWS Accounts
      • Manual setup
        • AWS CloudShell
      • Infra-as-code
        • AWS CloudFormation
        • Terraform
        • Pulumi
    • Creating S3 Import Rules
      • Configuration - Basic
      • Configuration - Optional Transformations
      • Previewing Imports
      • Regular Expressions in Import Rules
  • Using Scanner
    • Query Syntax
    • Aggregation Functions
      • avg()
      • count()
      • countdistinct()
      • eval()
      • groupbycount()
      • max()
      • min()
      • percentile()
      • rename()
      • stats()
      • sum()
      • table()
      • var()
      • where()
    • Detection Rules
      • Event Sinks
      • Out-of-the-Box Detection Rules
      • MITRE Tags
    • API
      • Ad hoc queries
      • Detection Rules
      • Event Sinks
      • Validating YAML files
    • Built-in Indexes
      • _audit
    • Role-Based Access Control (RBAC)
    • Beta features
      • Scanner for Splunk
        • Getting Started
        • Using Scanner Search Commands
        • Dashboards
        • Creating Custom Content in Splunk Security Essentials
      • Scanner for Grafana
        • Getting Started
      • Jupyter Notebooks
        • Getting Started with Jupyter Notebooks
        • Scanner Notebooks on Github
      • Detection Rules as Code
        • Getting Started
        • Writing Detection Rules
        • CLI
        • Managing Synced Detection Rules
      • Detection Alert Formatting
        • Customizing PagerDuty Alerts
      • Scalar Functions and Operators
        • coalesce()
        • if()
        • arr.join()
        • math.abs()
        • math.round()
        • str.uriencode()
  • Single Sign On (SSO)
    • Overview
    • Okta
      • Okta Workforce
      • SAML
  • Self-Hosted Scanner
    • Overview
Powered by GitBook
On this page
  • File requirements
  • Schema
  • Detection Rule
  • Example
  • Index Specifiers
  • Detection Rule Tags
  • Example
  • Detection Rule Event Sinks
  • Example
  • Alert Template
  • Example
  • Detection Rule Tests
  • Dataset
  • Example
  • Offline Validation

Was this helpful?

  1. Using Scanner
  2. Beta features
  3. Detection Rules as Code

Writing Detection Rules

PreviousGetting StartedNextCLI

Last updated 3 days ago

Was this helpful?

File requirements

  • Each detection rule should have its own YAML file. The file should validate against our Schema. Detection rules will not sync if there are errors parsing or validating the YAML.

  • The file name should end with .yaml or .yml extension. Files with other extensions will be ignored.

  • The file should have # schema: https://scanner.dev/schema/scanner-detection-rule.v1.json as its first line. This is how we determine whether to a YAML file is a detection rule. YAML files without this comment will be ignored.

Schema

The schema for Scanner detection rules is at .

The schema contains information on the properties, including descriptions, requirements, and valid values. This can be used as a reference to write your detection rules and to validate your YAML documents.

Detection Rule

A detection rule YAML consists of:

  • name - detection rule name

  • description - description of detection rule

  • enabled - whether the detection rule is enabled

  • severity - severity of the detection rule. Scanner uses standards for severity tags. Refer to the for more details.

  • query_text - the query for the detection rule

  • tags- associated tags for the detection rule. See , below.

  • time_range_s - the lookback period (in seconds). Must be minute granularity (for example, 60 seconds is valid, but 30 seconds is not).

  • run_frequency_s - how frequently to run the detection rule (in seconds). Must be minute granularity and <= time_range_s.

  • event_sink_keys - keys for event sinks to send detection events to. See , below.

  • alert_template- custom detection alert formatting. See below.

  • tests - a list of test cases and expected results to apply to this detection rule. See , below.

Example

Below is an example YAML for a detection rule. It looks for updates to AWS SecurityHub findings, which may indicate evasion of security measures.

It is configured to run every 60 seconds and look for possible threat activity in the last 5 minutes of logs. It will trigger alerts with High severity level and send them to the alert event sinks that are associated with the key high_severity_alerts.

# schema: https://scanner.dev/schema/scanner-detection-rule.v1.json
name: AWS SecurityHub Findings Evasion
enabled: true
description: |
  Detects the modification of the findings on SecurityHub.

  References:
  * https://docs.aws.amazon.com/cli/latest/reference/securityhub/

  False Positives:
  * System or Network administrator behaviors
  * DEV, UAT, SAT environment. You should apply this rule with PROD environment only.
severity: High
query_text: |
  %ingest.source_type="aws:cloudtrail"
  eventSource="securityhub.amazonaws.com"
  eventName=(
    BatchUpdateFindings
    DeleteInsight
    UpdateFindings
    UpdateInsight
  )
  | stats
    min(timestamp) as firstTime,
    max(timestamp) as lastTime,
    count() as eventCount
    by
    userIdentity.arn,
    eventSource,
    eventName,
    awsRegion
tags:
  - techniques.ta0005.defense_evasion
time_range_s: 300
run_frequency_s: 60
event_sink_keys:
  - high_severity_alerts
alert_template:
  info:
    - label: Severity
      value: "{{@alert.severity}}"
    - label: Description
      value: "{{@alert.description}}"
  actions:
    - label: "View Runbook"
      value: "https://runbooks.com/aws_security_hub_findings_evasion"
tests:
  - name: Test alert is triggered when SecurityHub findings modified
    now_timestamp: "2024-08-21T00:03:00.000Z"
    dataset_inline: |
      {"timestamp":"2024-08-21T00:02:30.000Z","%ingest.source_type":"aws:cloudtrail","eventSource":"securityhub.amazonaws.com","eventName":"BatchUpdateFindings","userIdentity":{"arn":"arn:aws:iam::123456789012:user/JohnDoe"},"awsRegion":"us-west-2"}
      {"timestamp":"2024-08-21T00:02:00.000Z","%ingest.source_type":"aws:cloudtrail","eventSource":"s3.amazonaws.com","eventName":"GetObject","userIdentity":{"arn":"arn:aws:iam::123456789012:user/JohnDoe"},"requestParameters":{"bucketName":"mybucket","key":"exampleobject"}}
      {"timestamp":"2024-08-21T00:01:50.000Z","%ingest.source_type":"aws:cloudtrail","eventSource":"securityhub.amazonaws.com","eventName":"UpdateFindings","userIdentity":{"arn":"arn:aws:iam::123456789012:user/JohnDoe"},"awsRegion":"us-west-2"}
      {"timestamp":"2024-08-21T00:01:30.000Z","%ingest.source_type":"aws:cloudtrail","eventSource":"securityhub.amazonaws.com","eventName":"DeleteInsight","userIdentity":{"arn":"arn:aws:iam::123456789012:user/JohnDoe"},"awsRegion":"us-west-2"}
      {"timestamp":"2024-08-21T00:01:20.000Z","%ingest.source_type":"aws:cloudtrail","eventSource":"lambda.amazonaws.com","eventName":"InvokeFunction","userIdentity":{"arn":"arn:aws:iam::123456789012:user/JohnDoe"},"requestParameters":{"functionName":"my-function"}}
      {"timestamp":"2024-08-21T00:01:10.000Z","%ingest.source_type":"aws:cloudtrail","eventSource":"securityhub.amazonaws.com","eventName":"UpdateInsight","userIdentity":{"arn":"arn:aws:iam::123456789012:user/JohnDoe"},"awsRegion":"us-west-2"}
    expected_detection_result: true
  - name: Test no alert is triggered when SecurityHub findings not modified
    now_timestamp: "2024-08-21T00:03:00.000Z"
    dataset_inline: |
      {"timestamp":"2024-08-21T00:02:30.000Z","%ingest.source_type":"aws:cloudtrail","eventSource":"s3.amazonaws.com","eventName":"GetObject","userIdentity":{"arn":"arn:aws:iam::123456789012:user/JohnDoe"},"requestParameters":{"bucketName":"mybucket","key":"exampleobject"}}
      {"timestamp":"2024-08-21T00:02:00.000Z","%ingest.source_type":"aws:cloudtrail","eventSource":"lambda.amazonaws.com","eventName":"InvokeFunction","userIdentity":{"arn":"arn:aws:iam::123456789012:user/JohnDoe"},"requestParameters":{"functionName":"my-function"}}
      {"timestamp":"2024-08-21T00:01:50.000Z","%ingest.source_type":"aws:cloudtrail","eventSource":"dynamodb.amazonaws.com","eventName":"PutItem","userIdentity":{"arn":"arn:aws:iam::123456789012:user/JohnDoe"},"requestParameters":{"tableName":"my-table"}}
      {"timestamp":"2024-08-21T00:01:20.000Z","%ingest.source_type":"aws:cloudtrail","eventSource":"rds.amazonaws.com","eventName":"CreateDBInstance","userIdentity":{"arn":"arn:aws:iam::123456789012:user/JohnDoe"},"requestParameters":{"dBInstanceIdentifier":"mydb"}}
    expected_detection_result: false

Index Specifiers

To include an index specifier in your query text, you must use the full index specifier format, which is @index={ UUID | "alias" }. While @index=alias works on Scanner web and API, this format cannot be used for detection rules because aliases can change.

To get the full index specifier format, tab-to-complete an index specifier in the Scanner search box and copy-paste it into your detection rule file.

Below is a valid index specifier:

query_text: |
  @index={ 00000000-0000-0000-0000-000000000000 | "cloudtrail_logs" }

Below are examples of invalid index specifiers. These will result in QueryParseErrors.

query_text: |
  @index=00000000-0000-0000-0000-000000000000
query_text: |
  @index=cloudtrail_logs

Using index rather than @index will search for a column called index rather than filter a Scanner index.

Detection Rule Tags

Tags are specified as an array in YAML. Each tag must begin with an ASCII letter, and contain only ASCII alphanumeric characters, ., -, or _.

Scanner has default MITRE tags that you can use or you can create your own. See MITRE Tags for the full list of default tags.

Example

Below is an example of a detection rule's tags. This should be in the same file as your detection rule.

tags:
    - techniques.ta0006.credential_access
    - tactics.t1586.compromise_accounts

Detection Rule Event Sinks

Event sink keys can just be specified as an array in YAML. Each event sink key must begin with an ASCII letter, and contain only ASCII alphanumeric characters, -, or _.

Example

Below is an example of a detection rule's event sink keys. This should be in the same file as your detection rule.

event_sink_keys:
    - high_severity_alerts
    - soar_response_flow

Alert Template

You can customize detection alerts by providing an alert_template. The alert_template has two optional objects: info and actions. Both accept arrays of items with label and value fields.

actions will appear as buttons in Slack and as links in Markdown. The values in the actions section should be URLs. If the value is an invalid URL or not a URL, the key/value will appear as text in the alert, rather than as a button or link.

Note: info and actions also accept pairs of strings, but this format is deprecated. We recommend migrating to the label and value format.

Example

Below is an example of an alert template. This should be in the same file as your detection rule.

alert_template:
  info:
    - label: Severity
      value: "{{@alert.severity}}"
    - label: Description
      value: "{{@alert.description}}"
  actions:
    - label: "View Runbook"
      value: "https://runbooks.com/aws_security_hub_findings_evasion"

Detection Rule Tests

You can specify tests in your YAML file to run against the detection rule.

A detection rule test consists of:

  • name - test name

  • now_timestamp - timestamp to start the test (optional)

  • dataset_inline - a list of JSON log events for the test

  • expected_detection_result - whether the given dataset triggers a detection event

If now_timestampis not provided, Scanner will use the timestamp of the latest log event as the now_timestamp. In both cases, the timestamp is rounded to the next run_frequency_s. For example, if run_frequency_sis 60 seconds and now_timestampis 2024-07-05T10:09:34.123Z, the timestamp will be rounded up to 2024-07-05T10:10:00.000Z(the next minute).

Detection rules will not sync if there are failing tests.

Dataset

Test datasets are a list of JSON log events. There are a few ways to write test datasets:

  1. You can export results from Scanner as JSON lines and use the results as a test dataset. To export results, run a query in Scanner and click the arrow next to Run and select JSON Lines under Export Results.

  2. You can copy-paste raw_events from Scanner search results. The copy-pasted result is not guaranteed to be valid JSON because values may not be properly escaped.

  3. You can write your own JSON log events. A valid JSON log event must contain a timestamp in RFC 3339 format. It can contain any number of key/value pairs. An example log event:

{"timestamp":"2024-07-05T10:12:34.123Z","errorMessage":"unauthorized","eventSource":"s3.amazonaws.com"}

Example

Below is an example of a detection rule test. This should be in the same file as your detection rule.

tests:
  - name: Test unauthorized event present
    now_timestamp: 2024-07-05T11:00:00.000Z
    dataset_inline: |
      {"timestamp":"2024-07-05T10:12:34.123Z","errorMessage":"unauthorized","eventSource":"s3.amazonaws.com"}
      {"timestamp":"2024-07-05T10:12:45.123Z","eventSource":"lambda.amazonaws.com"}
      {"timestamp":"2024-07-05T10:13:34.123Z","errorMessage":"unauthorized","eventSource":"s3.amazonaws.com"}
      {"timestamp":"2024-07-05T10:14:34.123Z","errorMessage":"unauthorized","eventSource":"s3.amazonaws.com"}
      {"timestamp":"2024-07-05T10:15:34.123Z","errorMessage":"unauthorized","eventSource":"s3.amazonaws.com"}
      {"timestamp":"2024-07-05T10:16:34.123Z","errorMessage":"unauthorized","eventSource":"s3.amazonaws.com"}
      {"timestamp":"2024-07-05T10:17:34.123Z","errorMessage":"unauthorized","eventSource":"s3.amazonaws.com"}
      {"timestamp":"2024-07-05T10:18:34.123Z","errorMessage":"unauthorized","eventSource":"s3.amazonaws.com"}
    expected_detection_result: true

Offline Validation

While working on your detection rules, it can be valuable to test them as-is without needing to push to Github and sync with Scanner. We provide tools to do so:

  • Scanner provides a Python CLI and public API endpoints that can be used to validate the rule files, and also to run the provided tests. For more information:

    • CLI

You can specify keys that identify which event sinks a detection rule should log events to. You can assign one or more actual event sinks to each key in the settings for the sync source; see .

See for more information on the info and actions sections, as well as information on templating values.

A JSON schema that describes detection rules can be found at , which can be used to validate the YAML files directly.

Detection Alert Formatting
https://scanner.dev/schema/scanner-detection-rule.v1.json
API docs
https://scanner.dev/schema/scanner-detection-rule.v1.json
OCSF Severity ID
schema
Detection Rule Tags
Detection Rule Event Sinks
Alert Template
Detection Rule Tests
Getting Started