Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ rules:
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD..."
}
`}}
# Page 1: initial request uses the 'ge' cursor filter (dynamic timestamp).
- path: /v1.0/security/alerts_v2
methods: [GET]
query_params:
$filter: "{initial:lastUpdateDateTime ge .*}"
$skip: 0
$top: 2
request_headers:
Expand Down Expand Up @@ -190,10 +192,14 @@ rules:
]
}
`}}
# Page 2: pagination advances the filter to 'gt <max lastUpdateDateTime of page 1>'
# while keeping $skip at 0. If the input still used $skip offsets it would send
# $skip=2 here, which no rule matches, and --exit-on-unmatched-rule would fail.
- path: /v1.0/security/alerts_v2
methods: [GET]
query_params:
$skip: 2
$filter: "lastUpdateDateTime gt 2023-10-20T09:54:10.4666667Z"
$skip: 0
$top: 2
request_headers:
Authorization:
Expand Down Expand Up @@ -354,10 +360,13 @@ rules:
]
}
`}}
# Page 3: filter advances to 'gt <max lastUpdateDateTime of page 2>'. The empty
# page (len < batch_size) stops pagination.
- path: /v1.0/security/alerts_v2
methods: [GET]
query_params:
$skip: 4
$filter: "lastUpdateDateTime gt 2023-10-20T09:54:11.4966667Z"
$skip: 0
$top: 2
request_headers:
Authorization:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ rules:
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD..."
}
`}}
# Page 1: initial request uses the 'ge' cursor filter (dynamic timestamp).
- path: /v1.0/security/incidents
methods: [GET]
query_params:
$filter: "{initial:lastUpdateDateTime ge .*}"
$skip: 0
$top: 2
request_headers:
Expand Down Expand Up @@ -158,7 +160,7 @@ rules:
"tenantId": "b3c1b5fc-828c-45fa-a1e1-10d74f6d6e9c",
"displayName": "Multi-stage incident involving Initial access & Command and control on multiple endpoints reported by multiple sources",
"createdDateTime": "2021-08-13T08:43:35.5533333Z",
"lastUpdateDateTime": "2021-09-30T09:35:45.1133333Z",
"lastUpdateDateTime": "2021-09-30T09:35:46.1133333Z",
"assignedTo": "KaiC@contoso.onmicrosoft.com",
"classification": "truePositive",
"determination": "multiStagedAttack",
Expand Down Expand Up @@ -236,10 +238,14 @@ rules:
]
}
`}}
# Page 2: pagination advances the filter to 'gt <max lastUpdateDateTime of page 1>'
# while keeping $skip at 0. If the input still used $skip offsets it would send
# $skip=2 here, which no rule matches, and --exit-on-unmatched-rule would fail.
- path: /v1.0/security/incidents
methods: [GET]
query_params:
$skip: 2
$filter: "lastUpdateDateTime gt 2021-09-30T09:35:46.1133333Z"
$skip: 0
$top: 2
request_headers:
Authorization:
Expand All @@ -261,7 +267,7 @@ rules:
"tenantId": "b3c1b5fc-828c-45fa-a1e1-10d74f6d6e9c",
"displayName": "Multi-stage incident involving Initial access & Command and control on multiple endpoints reported by multiple sources",
"createdDateTime": "2021-08-13T08:43:35.5533333Z",
"lastUpdateDateTime": "2021-09-30T09:35:45.1133333Z",
"lastUpdateDateTime": "2021-09-30T09:35:47.1133333Z",
"assignedTo": "KaiC@contoso.onmicrosoft.com",
"classification": "truePositive",
"determination": "multiStagedAttack",
Expand Down Expand Up @@ -366,7 +372,7 @@ rules:
"tenantId": "b3c1b5fc-828c-45fa-a1e1-10d74f6d6e9c",
"displayName": "Multi-stage incident involving Initial access & Command and control on multiple endpoints reported by multiple sources",
"createdDateTime": "2021-08-13T08:43:35.5533333Z",
"lastUpdateDateTime": "2021-09-30T09:35:45.1133333Z",
"lastUpdateDateTime": "2021-09-30T09:35:48.1133333Z",
"assignedTo": "KaiC@contoso.onmicrosoft.com",
"classification": "truePositive",
"determination": "multiStagedAttack",
Expand Down Expand Up @@ -434,10 +440,13 @@ rules:
]
}
`}}
# Page 3: filter advances to 'gt <max lastUpdateDateTime of page 2>'. The empty
# page (len < batch_size) stops pagination.
- path: /v1.0/security/incidents
methods: [GET]
query_params:
$skip: 4
$filter: "lastUpdateDateTime gt 2021-09-30T09:35:48.1133333Z"
$skip: 0
$top: 2
request_headers:
Authorization:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ rules:
- "application/json"
body: |-
{"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrN","token_type": "Bearer","not_before": 1549647431,"expires_in": 3600}
# Page 1: initial request uses the 'ge' cursor filter (dynamic timestamp).
- path: /v1.0/security/incidents
methods: [GET]
query_params:
$filter: "{initial:lastUpdateDateTime ge .*}"
$skip: 0
$top: 2
request_headers:
Expand Down Expand Up @@ -64,7 +66,7 @@ rules:
"tenantId": "b3c1b5fc-1234-abcd-5678-10d74f6d6e9c",
"displayName": "Multi-stage incident involving Initial access & Command and control on multiple endpoints reported by multiple sources",
"createdDateTime": "2021-08-13T08:43:35.5533333Z",
"lastUpdateDateTime": "2021-09-30T09:35:45.1133333Z",
"lastUpdateDateTime": "2021-09-30T09:35:46.1133333Z",
"assignedTo": "admin@contoso.onmicrosoft.com",
"classification": "truePositive",
"determination": "multiStagedAttack",
Expand All @@ -84,10 +86,13 @@ rules:
]
}
`}}
# Page 2: pagination advances the filter to 'gt <max lastUpdateDateTime of page 1>'
# while keeping $skip at 0.
- path: /v1.0/security/incidents
methods: [GET]
query_params:
$skip: 2
$filter: "lastUpdateDateTime gt 2021-09-30T09:35:46.1133333Z"
$skip: 0
$top: 2
request_headers:
Authorization:
Expand All @@ -110,7 +115,7 @@ rules:
"tenantId": "b3c1b5fc-1234-abcd-5678-10d74f6d6e9c",
"displayName": "Multi-stage incident involving Initial access & Command and control on multiple endpoints reported by multiple sources",
"createdDateTime": "2021-08-13T08:43:35.5533333Z",
"lastUpdateDateTime": "2021-09-30T09:35:45.1133333Z",
"lastUpdateDateTime": "2021-09-30T09:35:47.1133333Z",
"assignedTo": "example@contoso.onmicrosoft.com",
"classification": "truePositive",
"determination": "multiStagedAttack",
Expand All @@ -135,7 +140,7 @@ rules:
"tenantId": "b3c1b5fc-1234-abcd-5678-10d74f6d6e9c",
"displayName": "Multi-stage incident involving Initial access & Command and control on multiple endpoints reported by multiple sources",
"createdDateTime": "2021-08-13T08:43:35.5533333Z",
"lastUpdateDateTime": "2021-09-30T09:35:45.1133333Z",
"lastUpdateDateTime": "2021-09-30T09:35:48.1133333Z",
"assignedTo": "example@contoso.onmicrosoft.com",
"classification": "truePositive",
"determination": "multiStagedAttack",
Expand All @@ -155,10 +160,13 @@ rules:
]
}
`}}
# Page 3: filter advances to 'gt <max lastUpdateDateTime of page 2>'. The empty
# page (len < batch_size) stops pagination.
- path: /v1.0/security/incidents
methods: [GET]
query_params:
$skip: 4
$filter: "lastUpdateDateTime gt 2021-09-30T09:35:48.1133333Z"
$skip: 0
$top: 2
request_headers:
Authorization:
Expand Down
5 changes: 5 additions & 0 deletions packages/m365_defender/changelog.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# newer versions go on top
- version: "5.15.1"
changes:
- description: Fix `alert` and `incident` pagination to advance the `$filter` time boundary (`lastUpdateDateTime gt <max in page>`) with `$skip` held at 0, instead of incrementing `$skip`. This avoids the Graph security API `$skip <= 500` limit (which caused `400` errors and stalled catch-up under high volume) and drains a backlog in a single interval. Also correct the `alert` `batch_size` default from 2000 to 1000, matching the API `$top` cap.
type: bugfix
link: https://github.com/elastic/integrations/pull/20348
- version: "5.15.0"
changes:
- description: Use new `release` field for agentless deployment mode to establish as beta.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,12 @@ inputs:
do_not_log_failure: true
fail_on_template_error: true
target: url.params.$filter
value: '[[.last_response.url.params.Get "$filter"]]'
value: '[[if (eq (len .last_response.body.value) 2000)]]lastUpdateDateTime gt [[(index .last_response.body.value (add (toInt (len .last_response.body.value)) -1)).lastUpdateDateTime]][[end]]'
- set:
do_not_log_failure: true
fail_on_template_error: true
target: url.params.$skip
value: '[[if (eq (len .last_response.body.value) 2000)]][[add (toInt (.last_response.url.params.Get "$skip")) 2000]][[end]]'
value: "0"
response.split:
ignore_empty_value: true
target: body.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ inputs:
do_not_log_failure: true
fail_on_template_error: true
target: url.params.$filter
value: '[[.last_response.url.params.Get "$filter"]]'
value: '[[if (eq (len .last_response.body.value) 2000)]]lastUpdateDateTime gt [[(index .last_response.body.value (add (toInt (len .last_response.body.value)) -1)).lastUpdateDateTime]][[end]]'
- set:
do_not_log_failure: true
fail_on_template_error: true
target: url.params.$skip
value: '[[if (eq (len .last_response.body.value) 2000)]][[add (toInt (.last_response.url.params.Get "$skip")) 2000]][[end]]'
value: "0"
response.split:
ignore_empty_value: true
target: body.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,21 @@ request.transforms:
value: 'include-unknown-enum-members'
{{/if}}
response.pagination:
# The Graph security API caps $skip at 500 and $top at 1000, and alerts_v2 does
# not return @odata.nextLink, so we cannot page with a growing $skip offset.
# Instead, keep $skip at 0 and advance the $filter time boundary: on a full page
# (len == batch_size) move to 'lastUpdateDateTime gt <max lastUpdateDateTime in
# page>' (the last element, since $orderby is ascending). A non-full page renders
# an empty value, which stops pagination.
# https://github.com/microsoftgraph/microsoft-graph-docs-contrib/blob/main/api-reference/beta/resources/security-error-codes.md#constraints
- set:
target: url.params.$filter
value: '[[.last_response.url.params.Get "$filter"]]'
value: '[[if (eq (len .last_response.body.value) {{batch_size}})]]lastUpdateDateTime gt [[(index .last_response.body.value (add (toInt (len .last_response.body.value)) -1)).lastUpdateDateTime]][[end]]'
fail_on_template_error: true
do_not_log_failure: true
- set:
target: url.params.$skip
value: '[[if (eq (len .last_response.body.value) {{batch_size}})]][[add (toInt (.last_response.url.params.Get "$skip")) {{batch_size}}]][[end]]'
value: '0'
fail_on_template_error: true
do_not_log_failure: true
response.split:
Expand Down
4 changes: 2 additions & 2 deletions packages/m365_defender/data_stream/alert/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ streams:
- name: batch_size
type: integer
title: Batch Size
description: Batch size for the response of the Alert Security Graph API V2. The maximum supported batch size value is 2000.
default: 2000
description: Number of alerts to request per page ($top) from the Alert Security Graph API V2. The API caps $top at 1000; larger values are silently clamped, so the maximum effective batch size is 1000.
default: 1000
multi: false
required: true
show_user: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ inputs:
do_not_log_failure: true
fail_on_template_error: true
target: url.params.$filter
value: '[[.last_response.url.params.Get "$filter"]]'
value: '[[if (eq (len .last_response.body.value) 2000)]]lastUpdateDateTime gt [[(index .last_response.body.value (add (toInt (len .last_response.body.value)) -1)).lastUpdateDateTime]][[end]]'
- set:
do_not_log_failure: true
fail_on_template_error: true
target: url.params.$skip
value: '[[if (eq (len .last_response.body.value) 2000)]][[add (toInt (.last_response.url.params.Get "$skip")) 2000]][[end]]'
value: "0"
response.split:
ignore_empty_value: true
split:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ inputs:
do_not_log_failure: true
fail_on_template_error: true
target: url.params.$filter
value: '[[.last_response.url.params.Get "$filter"]]'
value: '[[if (eq (len .last_response.body.value) 50)]]lastUpdateDateTime gt [[(index .last_response.body.value (add (toInt (len .last_response.body.value)) -1)).lastUpdateDateTime]][[end]]'
- set:
do_not_log_failure: true
fail_on_template_error: true
target: url.params.$skip
value: '[[if (eq (len .last_response.body.value) 50)]][[add (toInt (.last_response.url.params.Get "$skip")) 50]][[end]]'
value: "0"
response.split:
ignore_empty_value: true
split:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ inputs:
do_not_log_failure: true
fail_on_template_error: true
target: url.params.$filter
value: '[[.last_response.url.params.Get "$filter"]]'
value: '[[if (eq (len .last_response.body.value) 50)]]lastUpdateDateTime gt [[(index .last_response.body.value (add (toInt (len .last_response.body.value)) -1)).lastUpdateDateTime]][[end]]'
- set:
do_not_log_failure: true
fail_on_template_error: true
target: url.params.$skip
value: '[[if (eq (len .last_response.body.value) 50)]][[add (toInt (.last_response.url.params.Get "$skip")) 50]][[end]]'
value: "0"
response.split:
ignore_empty_value: true
target: body.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,21 @@ request.transforms:
value: 'alerts'
{{/if}}
response.pagination:
# The Graph security API caps $skip at 500 and $top at 1000, and incidents do
# not return @odata.nextLink, so we cannot page with a growing $skip offset.
# Instead, keep $skip at 0 and advance the $filter time boundary: on a full page
# (len == batch_size) move to 'lastUpdateDateTime gt <max lastUpdateDateTime in
# page>' (the last element, since $orderby is ascending). A non-full page renders
# an empty value, which stops pagination.
# https://github.com/microsoftgraph/microsoft-graph-docs-contrib/blob/main/api-reference/beta/resources/security-error-codes.md#constraints
- set:
target: url.params.$filter
value: '[[.last_response.url.params.Get "$filter"]]'
value: '[[if (eq (len .last_response.body.value) {{batch_size}})]]lastUpdateDateTime gt [[(index .last_response.body.value (add (toInt (len .last_response.body.value)) -1)).lastUpdateDateTime]][[end]]'
fail_on_template_error: true
do_not_log_failure: true
- set:
target: url.params.$skip
value: '[[if (eq (len .last_response.body.value) {{batch_size}})]][[add (toInt (.last_response.url.params.Get "$skip")) {{batch_size}}]][[end]]'
value: '0'
fail_on_template_error: true
do_not_log_failure: true
response.split:
Expand Down
2 changes: 1 addition & 1 deletion packages/m365_defender/data_stream/incident/manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ streams:
- name: batch_size
type: integer
title: Batch Size
description: Batch size for the response of the Incident Security Graph API V2. The maximum supported batch size value is 50.
description: Number of incidents to request per page ($top) from the Incident Security Graph API V2. The API caps $top at 1000; larger values are silently clamped. A conservative default is used because each incident expands its alerts, which increases per-page memory.
default: 50
multi: false
required: true
Expand Down
2 changes: 1 addition & 1 deletion packages/m365_defender/manifest.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
format_version: "3.4.0"
name: m365_defender
title: Microsoft Defender XDR
version: "5.15.0"
version: "5.15.1"
description: Collect logs from Microsoft Defender XDR with Elastic Agent.
categories:
- "security"
Expand Down
Loading