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
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ default_stages: [pre-commit]
# This is a template for connector pre-commit hooks
repos:
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v4.3.0
rev: v4.4.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
Expand All @@ -27,7 +27,7 @@ repos:
- id: check-json
- id: check-yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.4
rev: v0.15.2
hooks:
- id: ruff
args: [ "--fix", "--unsafe-fixes"] # Allow unsafe fixes (ruff pretty strict about what it can fix)
Expand All @@ -50,7 +50,7 @@ repos:
- repo: https://github.com/returntocorp/semgrep
rev: v1.89.0
hooks:
- id: semgrep
- id: semgrep-docker
- repo: https://github.com/Yelp/detect-secrets
rev: v1.5.0
hooks:
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright (c) 2017-2025 Splunk Inc.
Copyright (c) 2017-2026 Splunk Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion NOTICE
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Splunk SOAR App: MS Graph for Office 365
Copyright (c) 2017-2025 Splunk Inc.
Copyright (c) 2017-2026 Splunk Inc.
Third Party Software Attributions:

@@@@============================================================================
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2216,7 +2216,7 @@ ______________________________________________________________________

Auto-generated Splunk SOAR Connector documentation.

Copyright 2025 Splunk Inc.
Copyright 2026 Splunk Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion __init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File: __init__.py
#
# Copyright (c) 2017-2025 Splunk Inc.
# Copyright (c) 2017-2026 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
24 changes: 12 additions & 12 deletions office365.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"name": "Anton Neledov"
}
],
"license": "Copyright (c) 2017-2025 Splunk Inc.",
"license": "Copyright (c) 2017-2026 Splunk Inc.",
"app_version": "4.1.0",
"utctime_updated": "2026-01-09T05:33:39.032641Z",
"package_name": "phantom_msgraphoffice365",
Expand Down Expand Up @@ -8752,17 +8752,13 @@
],
"pip39_dependencies": {
"wheel": [
{
"module": "PyJWT",
"input_file": "wheels/py3/PyJWT-2.10.1-py3-none-any.whl"
},
{
"module": "cffi",
"input_file": "wheels/py39/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"
},
{
"module": "cryptography",
"input_file": "wheels/py3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl"
"input_file": "wheels/py3/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl"
},
{
"module": "msal",
Expand All @@ -8772,6 +8768,10 @@
"module": "pycparser",
"input_file": "wheels/py3/pycparser-2.23-py3-none-any.whl"
},
{
"module": "pyjwt",
"input_file": "wheels/py3/pyjwt-2.11.0-py3-none-any.whl"
},
{
"module": "typing_extensions",
"input_file": "wheels/py3/typing_extensions-4.15.0-py3-none-any.whl"
Expand All @@ -8780,25 +8780,25 @@
},
"pip313_dependencies": {
"wheel": [
{
"module": "PyJWT",
"input_file": "wheels/py3/PyJWT-2.10.1-py3-none-any.whl"
},
{
"module": "cffi",
"input_file": "wheels/py313/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"
},
{
"module": "cryptography",
"input_file": "wheels/py3/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl"
"input_file": "wheels/py3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl"
},
{
"module": "msal",
"input_file": "wheels/py3/msal-1.34.0-py3-none-any.whl"
},
{
"module": "pycparser",
"input_file": "wheels/py3/pycparser-2.23-py3-none-any.whl"
"input_file": "wheels/py3/pycparser-3.0-py3-none-any.whl"
},
{
"module": "pyjwt",
"input_file": "wheels/py3/pyjwt-2.11.0-py3-none-any.whl"
}
]
}
Expand Down
30 changes: 23 additions & 7 deletions office365_connector.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File: office365_connector.py
#
# Copyright (c) 2017-2025 Splunk Inc.
# Copyright (c) 2017-2026 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -2018,17 +2018,17 @@ def _handle_get_email_properties(self, param):

return action_result.set_status(phantom.APP_SUCCESS, "Successfully fetched email")

def _manage_data_duplication(self, emails, total_ingested, limit, max_emails):
def _manage_data_duplication(self, emails, total_ingested, max_emails, actual_processed):
"""
This function handles the duplicate emails we get during the ingestion process.

:param emails: Processed emails
:param total_ingested: Total ingested emails till now
:param limit: Current pagination limit
:param max_emails: Max emails to ingest
:param actual_processed: Actual number of emails processed (excluding connector-level skipped emails)
:return: limit: next cycle pagination limit, total_ingested: Total ingested emails till now
"""
total_ingested_current_cycle = limit - self._duplicate_count
total_ingested_current_cycle = actual_processed - self._duplicate_count
total_ingested += total_ingested_current_cycle

remaining_count = max_emails - total_ingested
Expand Down Expand Up @@ -2119,8 +2119,13 @@ def _handle_on_poll(self, param):
# last modified time in the state file
email_index = 0 if ingest_manner == "latest first" else -1

# Prevent duplicate ingestion in cross-poll (last_email_id) and intra-batch (seen_ids)
last_email_id = self._state.get("last_email_id", None)
seen_ids = {last_email_id} if last_email_id else set()

while True:
self._duplicate_count = 0
client_side_skipped = 0
ret_val, emails = self._paginator(action_result, endpoint, limit=cur_limit, params=params)
if phantom.is_fail(ret_val):
return action_result.get_status()
Expand All @@ -2135,9 +2140,16 @@ def _handle_on_poll(self, param):
if self.is_poll_now():
self.save_progress("Ingesting all possible artifacts (ignoring maximum artifacts value) for POLL NOW")

# Process all emails, checking each against seen_ids to handle out-of-order delivery
for index, email in enumerate(emails):
email_id = email.get("id")
if email_id in seen_ids:
client_side_skipped += 1
continue
seen_ids.add(email_id)

try:
self.send_progress("Processing email # {} with ID ending in: {}".format(index + 1, email["id"][-10:]))
self.send_progress("Processing email # {} with ID ending in: {}".format(index + 1 - client_side_skipped, email["id"][-10:]))
ret_val = self._process_email_data(config, action_result, endpoint, email)
if phantom.is_fail(ret_val):
failed_email_ids += 1
Expand All @@ -2148,12 +2160,15 @@ def _handle_on_poll(self, param):
error_msg = _get_error_msg_from_exception(e, self)
self.debug_print(f"Exception occurred while processing email ID: {email.get('id')}. {error_msg}")

if failed_email_ids == total_emails:
actual_processed = total_emails - client_side_skipped
if failed_email_ids == actual_processed and actual_processed > 0:
return action_result.set_status(
phantom.APP_ERROR,
"Error occurred while processing all the email IDs",
)

# Store last processed email ID to prevent duplicates on next poll
self._state["last_email_id"] = emails[email_index]["id"]
if not self.is_poll_now():
last_time = datetime.strptime(emails[email_index]["lastModifiedDateTime"], O365_TIME_FORMAT).strftime(O365_TIME_FORMAT)
self._state["last_time"] = last_time
Expand All @@ -2164,12 +2179,13 @@ def _handle_on_poll(self, param):

# Duplication logic should only work for the oldest first order and if we have more data on the server.
if total_emails >= cur_limit and email_index == -1:
cur_limit, total_ingested = self._manage_data_duplication(emails, total_ingested, cur_limit, max_emails)
cur_limit, total_ingested = self._manage_data_duplication(emails, total_ingested, max_emails, actual_processed)
if not cur_limit:
break
else:
break
else:
self.save_state(deepcopy(self._state))
break

# Update the 'first_run' value only if the ingestion gets successfully completed
Expand Down
2 changes: 1 addition & 1 deletion office365_consts.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File: office365_consts.py
#
# Copyright (c) 2017-2025 Splunk Inc.
# Copyright (c) 2017-2026 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion office365_get_email.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% block widget_content %}
<!-- Main Start Block -->
<!-- File: office365_get_email.html
Copyright (c) 2017-2025 Splunk Inc.
Copyright (c) 2017-2026 Splunk Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion office365_get_rule.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% block widget_content %}
<!-- Main Start Block -->
<!-- File: office365_get_rule.html
Copyright (c) 2017-2025 Splunk Inc.
Copyright (c) 2017-2026 Splunk Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion office365_list_events.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% block widget_content %}
<!-- Main Start Block -->
<!-- File: office365_list_events.html
Copyright (c) 2017-2025 Splunk Inc.
Copyright (c) 2017-2026 Splunk Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion office365_list_rules.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% block widget_content %}
<!-- Main Start Block -->
<!-- File: office365_list_rules.html
Copyright (c) 2017-2025 Splunk Inc.
Copyright (c) 2017-2026 Splunk Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion office365_resolve_name.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% block widget_content %}
<!-- Main Start Block -->
<!-- File: office365_resolve_name.html
Copyright (c) 2017-2025 Splunk Inc.
Copyright (c) 2017-2026 Splunk Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion office365_run_query.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{% block widget_content %}
<!-- Main Start Block -->
<!-- File: office365_run_query.html
Copyright (c) 2017-2025 Splunk Inc.
Copyright (c) 2017-2026 Splunk Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion office365_view.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File: office365_view.py
#
# Copyright (c) 2017-2025 Splunk Inc.
# Copyright (c) 2017-2026 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion process_email.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# File: process_email.py
#
# Copyright (c) 2017-2025 Splunk Inc.
# Copyright (c) 2017-2026 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 2 additions & 0 deletions release_notes/unreleased.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
**Unreleased**

* Bug fixed during onpoll ingested duplicate identical data in emails and container creating. [PAPP-37552]
Binary file removed wheels/py3/PyJWT-2.10.1-py3-none-any.whl
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added wheels/py3/pycparser-3.0-py3-none-any.whl
Binary file not shown.
Binary file added wheels/py3/pyjwt-2.11.0-py3-none-any.whl
Binary file not shown.
Loading