Skip to content

Conversation

@efd6
Copy link
Contributor

@efd6 efd6 commented Aug 15, 2025

Proposed commit message

o365: tolerate string JSON encodings of objects in Actions field O365 may send the objects in the Actions field as their JSON encoding, so conditionally parse elements of the list when they are strings. This requires working around the absence of an 'if' for iteration processors in the foreach processor and the absence of a JSON parser in Painless. If the array is mixed, the ordering of entries will be altered. 

Note

Obviously, this is awful… the whole way down.

Checklist

  • I have reviewed tips for building integrations and this pull request is aligned with them.
  • I have verified that all data streams collect metrics or logs.
  • I have added an entry to my package's changelog.yml file.
  • I have verified that Kibana version constraints are current according to guidelines.
  • I have verified that any added dashboard complies with Kibana's Dashboard good practices

Author's Checklist

  • [ ]

How to test this PR locally

Related issues

Screenshots

@efd6 efd6 self-assigned this Aug 15, 2025
@efd6 efd6 added enhancement New feature or request Integration:o365 Microsoft Office 365 Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations] labels Aug 15, 2025
O365 may send the objects in the Actions field as their JSON encoding, so conditionally parse elements of the list when they are strings. This requires working around the absence of an 'if' for iteration processors in the foreach processor and the absence of a JSON parser in Painless.
@elastic-vault-github-plugin-prod

🚀 Benchmarks report

To see the full report comment with /test benchmark fullreport

@efd6 efd6 marked this pull request as ready for review August 15, 2025 00:29
@efd6 efd6 requested a review from a team as a code owner August 15, 2025 00:29
@elasticmachine
Copy link

Pinging @elastic/security-service-integrations (Team:Security-Service Integrations)

Comment on lines 120 to 165
# Ensure that o365.audit.Actions is an array of Map.
- script:
description: 'This is the first half of an implementation of `if: _ingest._value instanceof String` for the foreach below.'
if: ctx.o365audit?.Actions != null
tag: script_select_string_actions
source: |-
ctx._tmp = [:];
def actions = [];
ctx._tmp.action_strings = [];
if (!(ctx.o365audit.Actions instanceof List)) {
ctx.o365audit.Actions = [ctx.o365audit.Actions];
}
for (def e: ctx.o365audit.Actions) {
if (e instanceof Map) {
actions.add(e);
} else if (e instanceof String) {
ctx._tmp.action_strings.add(e);
}
}
if (actions.length == ctx.o365audit.Actions.length) {
ctx._tmp.remove("action_strings");
return
}
ctx.o365audit.Actions = actions;
- foreach:
tag: parse_string_actions_to_json
field: _tmp.action_strings
if: ctx._tmp?.action_strings != null
processor:
json:
field: _ingest._value
on_failure:
- append:
field: error.message
value: 'Processor {{{_ingest.on_failure_processor_type}}} with tag {{{_ingest.on_failure_processor_tag}}} in pipeline {{{_ingest.pipeline}}} failed with message: {{{_ingest.on_failure_message}}}'
- script:
description: 'This is the second half of an implementation of `if: _ingest._value instanceof String` for the foreach above.'
if: ctx._tmp?.action_strings != null
tag: script_select_string_actions
source: |-
ctx.o365audit = ctx.o365audit ?: [:];
ctx.o365audit.Actions = ctx.o365audit.Actions ?: [];
for (def e: ctx._tmp.action_strings) {
ctx.o365audit.Actions.add(e);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I played around with using a stream to partition. Not necessary to use.

 # Ensure that o365.audit.Actions is an array of Map. - script: description: 'This is the first half of an implementation of `if: _ingest._value instanceof String` for the foreach below.' if: ctx.o365audit?.Actions != null tag: script_select_string_actions source: |- if (!(ctx.o365audit.Actions instanceof List)) { ctx.o365audit.Actions = [ctx.o365audit.Actions]; } Map parts = ctx.o365audit?.Actions.stream().collect(Collectors.partitioningBy(x -> x instanceof String)); ctx._tmp_action_strings = parts.get(true); ctx.o365audit.Actions = parts.get(false) ?: []; - foreach: tag: parse_string_actions_to_json field: _tmp_action_strings if: ctx._tmp_action_strings != null processor: json: field: _ingest._value on_failure: - append: field: error.message value: 'Processor {{{_ingest.on_failure_processor_type}}} with tag {{{_ingest.on_failure_processor_tag}}} in pipeline {{{_ingest.pipeline}}} failed with message: {{{_ingest.on_failure_message}}}' - script: description: 'This is the second half of an implementation of `if: _ingest._value instanceof String` for the foreach above.' if: ctx._tmp_action_strings != null tag: script_select_string_actions source: |- ctx.o365audit.Actions.addAll(ctx._tmp_action_strings); - remove: field: _tmp_action_strings ignore_missing: true 
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've taken the finaliser, but I prefer the setup that exists.

@elasticmachine
Copy link

💚 Build Succeeded

History

cc @efd6

@efd6 efd6 merged commit b442ab4 into elastic:main Aug 17, 2025
9 checks passed
@elastic-vault-github-plugin-prod

Package o365 - 2.24.0 containing this change is available at https://epr.elastic.co/package/o365/2.24.0/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request Integration:o365 Microsoft Office 365 Team:Security-Service Integrations Security Service Integrations team [elastic/security-service-integrations]

3 participants