Skip to main content

Sigma Arrays

Overview

Standard Sigma does not natively support matching values within arrays in log events. This limitation affects many common log sources including CloudTrail, GCP Audit Logs, Okta, and Kubernetes audit logs, all of which use arrays extensively in their event structures. AlphaSOC extends Sigma with array matching capabilities as a custom feature, enabling detection rules that evaluate conditions against individual array elements.

info

Array support is currently available for raw event processing only. OCSF-normalized events do not support array matching at this time.

Array Matching Syntax

Array matching in Sigma uses nested detection blocks. When a field references an array, you define a nested condition and selection structure that specifies how array elements should be matched. This approach allows for multiple selectors on a single array object, including NOT conditions.

Match any (default)

By default, array matching returns true if any element in the array matches the condition:

detection:
condition: selection
selection:
arrayField:
condition: selection
selection:
fieldInArrayObject: "value"

Match all

Use the |arrayAll modifier to require that all elements in the array match the condition:

detection:
condition: selection
selection:
arrayField|arrayAll:
condition: selection
selection:
fieldInArrayObject: "value"

Nested arrays

For log sources with deeply nested array structures, you can chain array matching across multiple levels:

detection:
condition: selection
selection:
authorizationInfo|arrayAll:
condition: selection
selection:
resourceAttributes:
condition: selection
selection:
values|arrayAll:
condition: selection
selection:
key: "request.auth.claims.groups"
value|contains: "system:authenticated"

Dot pseudo-field

Use the . pseudo-field to match simple values (strings or numbers) directly within an array:

detection:
condition: selection
selection:
eventType: "AwsApiCall"
resourceTypeFilters:
condition: selection
selection:
.|startswith: "prefix:"

Real-World Examples

AWS RDS snapshot sharing

Consider the following AWS CloudTrail event, generated when an IAM user shares a database snapshot with an external AWS account:

{
"eventVersion": "1.08",
"userIdentity": {
"type": "IAMUser",
"arn": "arn:aws:iam::111122223333:user/db-admin"
},
"eventTime": "2026-04-07T16:22:11Z",
"eventSource": "rds.amazonaws.com",
"eventName": "ModifyDBSnapshotAttribute",
"awsRegion": "us-east-1",
"sourceIPAddress": "203.0.113.50",
"userAgent": "aws-cli/2.15.0",
"requestParameters": {
"dBSnapshotIdentifier": "prod-finance-snapshot-2026-04-07",
"attributeName": "restore",
"valuesToAdd": ["999900001111"]
},
"responseElements": null
}

The key field here is requestParameters.valuesToAdd—an array of AWS account IDs being granted restore access to this snapshot. AWS also accepts the special string all to make a snapshot publicly restorable.

A detection rule needs to flag two threats:

  1. The snapshot is made public (the array contains all).
  2. The snapshot is shared with an unknown external account (an account ID that doesn't belong to the organization).

Using our array extensions, both conditions can be expressed in one rule:

detection:
condition:
selection_event and (selection_public or selection_allowed_accounts)
selection_event:
eventSource: rds.amazonaws.com
eventName:
- ModifyDBSnapshotAttribute
- ModifyDBClusterSnapshotAttribute
requestParameters.attributeName: restore
selection_public:
requestParameters.valuesToAdd:
condition: sel
sel:
.: all
selection_allowed_accounts:
requestParameters.valuesToAdd|arrayAll:
condition: not sel
sel:
.:
- 111122223333
- 444455556666
- 777788889999
- 123456789012
  • selection_public: uses the . pseudo-field to iterate the array and checks whether any element equals all.
  • selection_allowed_accounts: uses the . pseudo-field to check whether any element matches a known internal account ID. The not in the condition inverts this—so the rule fires when at least one account ID in the array is not in the allowlist, i.e. it belongs to an unknown external party.

In the example event above, 999900001111 is not in the internal account list, so not sel is true, and the alert fires.

GitHub repository ruleset bypass

Consider the following GitHub audit log event, generated when an administrator modifies a repository ruleset to exclude the main branch from protection:

{
"@timestamp": 1775557901285,
"action": "repository_ruleset.update",
"actor": "octo-admin",
"actor_id": 1234567,
"org": "octo-org",
"repo": "octo-org/payments-api",
"operation_type": "modify",
"ruleset_id": 9876543,
"ruleset_name": "Protect default branch",
"ruleset_enforcement": "enabled",
"ruleset_source_type": "Repository",
"ruleset_conditions_updated": [
{
"target": "ref_name",
"old_parameters": {
"include": ["refs/heads/main"],
"exclude": []
},
"parameters": {
"include": [],
"exclude": ["refs/heads/main"]
}
}
]
}

The key field here is ruleset_conditions_updated—an array of condition changes applied to the ruleset. Each element contains a target field identifying what the condition applies to, along with old_parameters and parameters objects indicating the before and after state of the update.

We want to detect if the main branch was excluded in the updated parameters. To do that, the detection needs to flag when refs/heads/main appears in the parameters.exclude array:

detection:
condition: selection
selection:
action: repository_ruleset.update
ruleset_conditions_updated:
condition: selection
selection:
target: ref_name
parameters.exclude:
condition: selection
selection:
.: refs/heads/main
  • ruleset_conditions_updated: iterates the array of condition changes.
  • target: ref_name: matches only changes affecting branch name conditions.
  • parameters.exclude / .: refs/heads/main: uses nested array matching with the . pseudo-field to check if refs/heads/main appears in the exclusion list.

In the example event above, the main branch was excluded in the updated parameters, so the detection fires.

Slack login from new ASN

Consider the following Slack audit log event, generated when a user logs in from an IP address associated with a previously unseen ASN:

{
"id": "Ev04ABCDEF",
"date_create": 1717430400,
"action": "user_login",
"actor": {
"type": "user",
"user": {
"id": "U01234567",
"email": "alice@example.com"
}
},
"context": {
"ip_address": "203.0.113.42",
"ua": "Mozilla/5.0"
},
"details": {
"ip_address_details": ["new-asn", "vpn"]
}
}

The key field here is details.ip_address_details—an array of tags describing characteristics of the source IP address. Tags like new-asn or vpn provide context about the network origin.

A detection rule needs to flag logins from networks not previously seen for this workspace, which could indicate account compromise or credential theft:

detection:
condition: selection
selection:
details.ip_address_details:
condition: selection
selection:
.: new-asn
  • details.ip_address_details: targets the array of IP characteristics.
  • .: new-asn: uses the . pseudo-field to check whether any element in the array equals new-asn.

In the example event above, the array contains new-asn, so the detection fires. The presence of vpn is noted but doesn't affect this particular rule.

Limitations

  • OCSF not supported: Array matching is currently available only for raw event processing. OCSF-normalized events do not support this feature.

Further Reading